Skip to main content

rucc_ir/
verify.rs

1//! The verifier: whether a module is one the rest of the compiler may believe.
2//!
3//! Design: `spec/08-ir.md` section 8.7.
4//!
5//! This is not a debug aid. It runs after every pass in a debug build, after every pass in CI,
6//! and on demand with `-fverify-ir`, and a pass that produces IR failing it is a build failure
7//! rather than a warning. The reason is that a pass which breaks an invariant does not usually
8//! produce wrong output there and then. It produces IR that a later pass reads under an
9//! assumption that no longer holds, and the wrong instruction comes out somewhere else
10//! entirely. Finding it here is the difference between a two-line diagnosis and a week.
11//!
12//! # What it checks
13//!
14//! Dominance, so that every use is reached by its definition. Block parameter arity and types
15//! against every branch that arrives. Terminator placement. Operand and result types, by the
16//! rules in section 8.2. That no instruction refers to a value whose definition has been taken
17//! out of the function. That `alloca` is in the entry block unless its size is dynamic. That an
18//! ordering is one the operation can be asked for. That the metadata is a tree. That every flag
19//! is one the opcode reads. That every block is reachable. There is no separate rule that `asm
20//! goto` ends its block, because inline assembly with labels is a terminator and terminator
21//! placement is the rule that says so.
22//!
23//! # What it does not check
24//!
25//! Whether a name resolves. A module is one translation unit and a symbol it calls or takes the
26//! address of is usually defined in another, so an unresolved name is the linker's question,
27//! not this one. What is checked is the part that is here: where a direct call names a function
28//! this module also holds, the signature at the call has to be the signature of that function.
29//!
30//! Side table indices are trusted. A `Sig` or the index of a `MemInfo` can only come from the
31//! method that appended it, so a bad one is not a thing a pass can produce by accident. Values
32//! and blocks are different, because a pass builds those by hand from indices it worked out
33//! itself, so those are bounds-checked before anything else looks at them.
34//!
35//! # Errors, plural
36//!
37//! Every problem is reported, not just the first. The parser stops at the first thing that does
38//! not add up because a malformed text has one author and one mistake. A module failing
39//! verification was produced by a pass, and the shape of the whole failure is what says which
40//! rewrite went wrong.
41
42use std::fmt;
43
44use rucc_base::Interner;
45use rucc_target::TargetInfo;
46
47use crate::func::Func;
48use crate::inst::{Abi, Block, Def, Inst, Param, Signature, Value};
49use crate::module::{Alias, AliasKind, DataLayout, Datum, Global, Module, SymbolRef};
50use crate::{Extra, MemOrder, Opcode, Type};
51
52/// One thing wrong with a module.
53#[derive(Clone, Debug, PartialEq, Eq)]
54pub struct VerifyError {
55    /// What it is about, as `@sum block1 add`, or `@counter` for a global, or `!1` for a
56    /// metadata node.
57    pub at: String,
58    /// What is wrong with it.
59    pub message: String,
60}
61
62impl fmt::Display for VerifyError {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        write!(f, "{}: {}", self.at, self.message)
65    }
66}
67
68impl std::error::Error for VerifyError {}
69
70/// Checks a whole module.
71///
72/// # Errors
73///
74/// Gives back everything wrong with it, in the order the module is walked, which is the globals
75/// then the aliases then the functions then the metadata.
76pub fn verify(module: &Module, names: &Interner) -> Result<(), Vec<VerifyError>> {
77    let mut verifier = Verifier::new(module, names);
78    verifier.module();
79    verifier.finish()
80}
81
82/// Checks one function, and nothing else in the module it is in.
83///
84/// This is what a pass calls after rewriting a function, since walking the whole module after
85/// every function of it would be quadratic.
86///
87/// # Errors
88///
89/// Gives back everything wrong with that function.
90pub fn verify_func<'a>(
91    module: &'a Module,
92    func: &'a Func,
93    names: &'a Interner,
94) -> Result<(), Vec<VerifyError>> {
95    let mut verifier = Verifier::new(module, names);
96    verifier.func(func);
97    verifier.finish()
98}
99
100/// Checking one module.
101struct Verifier<'a> {
102    module: &'a Module,
103    names: &'a Interner,
104    errors: Vec<VerifyError>,
105    /// Where the walk is, which is what an error is labelled with. Built into a string only
106    /// when something is actually wrong, because the common case is that nothing is.
107    func: Option<&'a Func>,
108    block: Option<Block>,
109    inst: Option<Inst>,
110}
111
112impl<'a> Verifier<'a> {
113    fn new(module: &'a Module, names: &'a Interner) -> Self {
114        Verifier { module, names, errors: Vec::new(), func: None, block: None, inst: None }
115    }
116
117    fn finish(self) -> Result<(), Vec<VerifyError>> {
118        if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
119    }
120
121    // The module.
122
123    fn module(&mut self) {
124        let implied = DataLayout::for_target(&TargetInfo::new(self.module.triple));
125        if self.module.datalayout != implied {
126            self.at(
127                format!("@{}", self.names.resolve(self.module.name)),
128                format!(
129                    "the datalayout is `{}` and {} implies `{implied}`",
130                    self.module.datalayout, self.module.triple
131                ),
132            );
133        }
134
135        for id in self.module.globals() {
136            self.global(&self.module[id]);
137        }
138        for id in self.module.aliases() {
139            self.alias(&self.module[id]);
140        }
141        for id in self.module.funcs() {
142            self.func(&self.module[id]);
143        }
144        self.metadata();
145    }
146
147    fn global(&mut self, global: &Global) {
148        let at = format!("@{}", self.names.resolve(global.name));
149        if !global.align.is_power_of_two() {
150            self.at(
151                at.clone(),
152                format!("an alignment is a power of two and this is {}", global.align),
153            );
154        }
155        // A global with no image is a declaration of something another module defines, and
156        // there is nothing here to check about it. It may be constant: `extern const char
157        // *const sys_errlist[];` is a declaration of an object that lives in the library's
158        // read only data, and whether writing through a pointer to it is undefined is a fact
159        // about the object rather than about which module holds the bytes.
160        let Some(init) = global.init else { return };
161        let mut size = 0;
162        for &datum in &self.module[init] {
163            size += datum.size(self.module);
164            // An image is bytes and a scalar in one has to say how many it takes. `ptr` does
165            // not: the width of an address is the target's and not the type's, which is what
166            // makes a `ptr` here a scalar of no size that silently contributes nothing. An
167            // address in an image is [`Datum::Addr`], and a number the program wrote as one is
168            // the integer it is.
169            if let Datum::Scalar { ty, .. } = datum {
170                if ty.bits() == 0 {
171                    self.at(
172                        at.clone(),
173                        format!("a scalar in an image has a width and {ty} has none"),
174                    );
175                }
176            }
177            if let Datum::Addr(reloc) = datum {
178                let bytes = self.module[reloc].size;
179                if !matches!(bytes, 1 | 2 | 4 | 8) {
180                    self.at(
181                        at.clone(),
182                        format!(
183                            "an address is written as 1, 2, 4 or 8 bytes and this one as {bytes}"
184                        ),
185                    );
186                }
187            }
188        }
189        if size != global.size {
190            self.at(at, format!("the image is {size} bytes and the global is {}", global.size));
191        }
192    }
193
194    fn alias(&mut self, alias: &Alias) {
195        let at = format!("@{}", self.names.resolve(alias.name));
196        if alias.name == alias.target {
197            self.at(at, "an alias to itself");
198            return;
199        }
200        // Only what this module holds is checked. A target defined in another object is what
201        // an alias to a weak symbol looks like, and whether it resolves is the linker's answer.
202        let Some(found) = self.module.lookup(alias.target) else { return };
203        if alias.kind == AliasKind::IFunc && !matches!(found, SymbolRef::Func(_)) {
204            self.at(at, "an ifunc resolves through a function and this target is not one");
205        }
206    }
207
208    fn metadata(&mut self) {
209        for node in self.module.metadata() {
210            let Some(parent) = self.module[node].parent else { continue };
211            if parent.raw() >= node.raw() {
212                // A parent that comes later cannot be a tree, and a parent that is the node
213                // itself is the cycle the walk up a TBAA tree would never leave.
214                self.at(
215                    format!("!{}", node.raw()),
216                    format!(
217                        "a metadata node's parent comes before it and this one is !{}",
218                        parent.raw()
219                    ),
220                );
221            }
222        }
223    }
224
225    // The function.
226
227    fn func(&mut self, func: &'a Func) {
228        self.func = Some(func);
229        self.block = None;
230        self.inst = None;
231
232        if let Some((one, other)) = func.attrs.conflict() {
233            self.error(format!("`{one}` and `{other}` cannot both be true of a function"));
234        }
235        if func.is_declaration() {
236            self.func = None;
237            return;
238        }
239        // Everything after this indexes with values and blocks read out of the function, so
240        // nothing does until they are all known to be in range.
241        if !self.bounds(func) {
242            self.func = None;
243            return;
244        }
245
246        for signature in func.signatures() {
247            self.signature(signature);
248        }
249
250        let entry = func.entry().expect("a function with blocks has a first one");
251        let params: Vec<Type> = func[entry].params.iter().map(|&value| func[value].ty).collect();
252        let want: Vec<Type> = func.signature().param_types().collect();
253        if params != want {
254            self.error(format!(
255                "the entry block takes {} and the signature says {}",
256                types(&params),
257                types(&want)
258            ));
259        }
260
261        let doms = Doms::new(func);
262        let layout = Layout::new(func);
263        for block in func.blocks() {
264            self.block = Some(block);
265            self.block(func, block, &doms, &layout);
266        }
267        self.block = None;
268        self.inst = None;
269        self.func = None;
270    }
271
272    /// What the ABI asks of a signature's parameters agrees with what they are.
273    ///
274    /// None of this is about the target. Whether a `struct` of twenty four bytes travels as its
275    /// own bytes or as the address of a copy is the classification's answer and this has no
276    /// opinion on it, but a `byval` on something that is not a pointer describes no call on any
277    /// target, and neither does a second `sret`.
278    fn signature(&mut self, signature: &Signature) {
279        for (index, param) in signature.params.iter().enumerate() {
280            let at = format!("parameter {}", index + 1);
281            self.abi(&at, param);
282            match param.abi {
283                Abi::Sret { .. } if index > 0 => {
284                    // It is the address the return value goes to, so it arrives before anything
285                    // the function was called with. A later one is a different calling
286                    // convention wearing the same word.
287                    self.error("sret is the first parameter and this one is not");
288                }
289                Abi::Sret { .. } if !signature.returns.is_empty() => {
290                    self.error("a signature returning through sret returns nothing else");
291                }
292                _ => {}
293            }
294        }
295        for (index, param) in signature.returns.iter().enumerate() {
296            let at = format!("result {}", index + 1);
297            self.abi(&at, param);
298            if param.abi.indirect() {
299                // A return value too large for the registers comes back through an `sret`
300                // parameter, which is a parameter and is checked as one.
301                self.error(format!("{at} travels indirectly and a result cannot"));
302            }
303        }
304    }
305
306    /// One parameter's attribute against its type.
307    fn abi(&mut self, at: &str, param: &Param) {
308        match param.abi {
309            Abi::Plain => {}
310            Abi::Sext | Abi::Zext => {
311                if !param.ty.is_int() || param.ty.is_vector() {
312                    self.error(format!("{at} is extended and {} is not an integer", param.ty));
313                }
314            }
315            Abi::ByVal { size, align } | Abi::Sret { size, align } => {
316                if !param.ty.is_ptr() {
317                    self.error(format!(
318                        "{at} travels indirectly and {} is not a pointer",
319                        param.ty
320                    ));
321                }
322                if !align.is_power_of_two() {
323                    self.error(format!("an alignment is a power of two and this is {align}"));
324                }
325                if size == 0 {
326                    self.error(format!("{at} travels indirectly and has no size"));
327                }
328            }
329        }
330    }
331
332    /// Every value and every block an instruction names is one the function has.
333    ///
334    /// This is separate and comes first because everything else reads a `ValueData` or a
335    /// `BlockData` out of a table, and an index past the end of one is a panic rather than a
336    /// diagnosis.
337    fn bounds(&mut self, func: &'a Func) -> bool {
338        let counts = func.counts();
339        let before = self.errors.len();
340        for block in func.blocks() {
341            self.block = Some(block);
342            for &value in &func[block].params {
343                if value.index() >= counts.values {
344                    self.error(format!(
345                        "parameter %{} is not a value of this function",
346                        value.raw()
347                    ));
348                }
349            }
350            for inst in func.insts(block) {
351                self.inst = Some(inst);
352                for &value in &func[func[inst].args] {
353                    if value.index() >= counts.values {
354                        self.error(format!("%{} is not a value of this function", value.raw()));
355                    }
356                }
357                for value in func[inst].results() {
358                    if value.index() >= counts.values {
359                        self.error(format!("%{} is not a value of this function", value.raw()));
360                    }
361                }
362                for call in func.successors(inst) {
363                    if call.block.index() >= counts.blocks {
364                        self.error(format!(
365                            "block{} is not a block of this function",
366                            call.block.raw()
367                        ));
368                    }
369                    for &value in &func[call.args] {
370                        if value.index() >= counts.values {
371                            self.error(format!("%{} is not a value of this function", value.raw()));
372                        }
373                    }
374                }
375            }
376            self.inst = None;
377        }
378        self.block = None;
379        self.errors.len() == before
380    }
381
382    fn block(&mut self, func: &'a Func, block: Block, doms: &Doms, layout: &Layout) {
383        if !doms.reaches(block) {
384            self.error("this block is not reachable and has not been deleted");
385        }
386        if block == func.entry().expect("checked in func") {
387            for other in func.blocks() {
388                let last = func[other].last;
389                if last.is_some_and(|inst| func.successors(inst).any(|call| call.block == block)) {
390                    self.error("the entry block is branched to, and it takes the arguments");
391                }
392            }
393        }
394
395        let mut seen_terminator = false;
396        for inst in func.insts(block) {
397            self.inst = Some(inst);
398            if seen_terminator {
399                self.error("this comes after the block's terminator");
400            }
401            seen_terminator |= func.is_terminator(inst);
402            self.inst(func, inst, doms, layout);
403        }
404        self.inst = None;
405        if !seen_terminator {
406            self.error("this block does not end in a terminator");
407        }
408    }
409
410    fn inst(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
411        let data = &func[inst];
412        let opcode = data.opcode;
413
414        let stray = data.flags.without(crate::Flags::legal_on(opcode));
415        if !stray.is_empty() {
416            let names: Vec<&str> = stray.iter().map(|(_, name)| name).collect();
417            self.error(format!("{} does not read `{}`", opcode.name(), names.join("`, `")));
418        }
419        if data.extra.kind() != opcode.extra_kind() {
420            // An instruction carrying some other opcode's payload prints as text the parser
421            // cannot read, so this is caught here rather than found as a round trip failure.
422            self.error(format!(
423                "{} carries {} and this one carries {}",
424                opcode.name(),
425                opcode.extra_kind().name(),
426                data.extra.kind().name()
427            ));
428        }
429        if let Some(want) = opcode.results() {
430            if want != data.results {
431                self.error(format!(
432                    "{} produces {want} values and this one produces {}",
433                    opcode.name(),
434                    data.results
435                ));
436            }
437        }
438
439        self.uses(func, inst, doms, layout);
440        self.branches(func, inst);
441        self.memory(func, inst);
442        self.shape(func, inst);
443    }
444
445    /// Every use is reached by its definition.
446    fn uses(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
447        let block = layout.block_of(inst).expect("walking the blocks");
448        let check = |verifier: &mut Self, value: Value| match func[value].def {
449            Def::Param { block: def, .. } => {
450                if !doms.dominates(def, block) {
451                    verifier.error(format!(
452                        "%{} arrives at block{} and does not reach here",
453                        value.raw(),
454                        def.raw()
455                    ));
456                }
457            }
458            Def::Result { inst: def, .. } => {
459                let Some(def_block) = layout.block_of(def) else {
460                    verifier.error(format!(
461                        "%{} is produced by an instruction that is not in the function",
462                        value.raw()
463                    ));
464                    return;
465                };
466                let reaches = if def_block == block {
467                    layout.position(def) < layout.position(inst)
468                } else {
469                    doms.dominates(def_block, block)
470                };
471                if !reaches {
472                    verifier.error(format!(
473                        "%{} is produced in block{} and does not reach here",
474                        value.raw(),
475                        def_block.raw()
476                    ));
477                }
478            }
479        };
480        for &value in &func[func[inst].args] {
481            check(self, value);
482        }
483        for call in func.successors(inst) {
484            for &value in &func[call.args] {
485                check(self, value);
486            }
487        }
488    }
489
490    /// Every branch passes what the block it goes to takes.
491    fn branches(&mut self, func: &'a Func, inst: Inst) {
492        if func[inst].opcode == Opcode::BlockAddr {
493            // The one instruction that names a block without arriving at it, so the block's
494            // parameters are nothing to do with it. That it passes no arguments is checked
495            // with the rest of its shape.
496            return;
497        }
498        for call in func.successors(inst) {
499            let params = &func[call.block].params;
500            let args = &func[call.args];
501            if params.len() != args.len() {
502                self.error(format!(
503                    "block{} takes {} arguments and this branch passes {}",
504                    call.block.raw(),
505                    params.len(),
506                    args.len()
507                ));
508                continue;
509            }
510            for (index, (&param, &arg)) in params.iter().zip(args).enumerate() {
511                let (want, got) = (func[param].ty, func[arg].ty);
512                if want != got {
513                    self.error(format!(
514                        "argument {} to block{} is {want} and this one is {got}",
515                        index + 1,
516                        call.block.raw()
517                    ));
518                }
519            }
520        }
521        if let Extra::Switch(info) = func[inst].extra {
522            let switch = &func[info];
523            let targets = &func[switch.targets];
524            let cases = &func[switch.cases];
525            if targets.len() != cases.len() + 1 {
526                self.error(format!(
527                    "a switch has one target per case and a default, and this one has {} targets for {} cases",
528                    targets.len(),
529                    cases.len()
530                ));
531            }
532            for (index, case) in cases.iter().enumerate() {
533                if cases[..index].contains(case) {
534                    self.error("two cases of this switch have the same value");
535                }
536            }
537        }
538    }
539
540    /// What an access says about itself.
541    fn memory(&mut self, func: &'a Func, inst: Inst) {
542        let opcode = func[inst].opcode;
543        let info = match func[inst].extra {
544            Extra::Mem(at) => func[at],
545            Extra::Rmw(_, at) => func[at],
546            Extra::Order(order) => {
547                if !order.is_valid_for_rmw() {
548                    self.error("a fence is not a fence unless it orders something");
549                }
550                return;
551            }
552            _ => return,
553        };
554        if !info.align.is_power_of_two() {
555            self.error(format!("an alignment is a power of two and this is {}", info.align));
556        }
557        if let Some(tbaa) = info.tbaa {
558            if tbaa.index() >= self.module.counts().metadata {
559                self.error(format!("!{} is not a metadata node of this module", tbaa.raw()));
560            }
561        }
562        let ok = match opcode {
563            Opcode::AtomicLoad => info.order.is_valid_for_load(),
564            Opcode::AtomicStore => info.order.is_valid_for_store(),
565            Opcode::AtomicRmw | Opcode::Cmpxchg => info.order.is_valid_for_rmw(),
566            // Everything else is the non-atomic form, and the atomic form is a different
567            // opcode, so an ordering here is one somebody meant to put on that one.
568            _ => info.order == MemOrder::NotAtomic,
569        };
570        if !ok {
571            self.error(format!("{} cannot be asked for {}", opcode.name(), info.order));
572        }
573        if matches!(opcode, Opcode::Memcpy | Opcode::Memmove | Opcode::Memset) && info.size == 0 {
574            self.error(format!("{} moves no bytes", opcode.name()));
575        }
576    }
577
578    /// The operand and result types, by the rules of section 8.2.
579    ///
580    /// The shape of each group is written out rather than derived from a table, because the
581    /// groups do not have the same shape as each other and a table general enough to hold all
582    /// of them would be harder to read than this.
583    #[expect(clippy::too_many_lines, reason = "one arm per group of opcodes, and they differ")]
584    fn shape(&mut self, func: &'a Func, inst: Inst) {
585        let data = &func[inst];
586        let opcode = data.opcode;
587        let args = &func[data.args];
588        let arity = args.len();
589        let arg = |n: usize| func[args[n]].ty;
590        let results = usize::from(data.results);
591        let res = |n: usize| func[data.results().nth(n).expect("within the count")].ty;
592
593        match opcode {
594            // Constants take nothing and say their own type.
595            Opcode::IConst => {
596                if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_int() {
597                    self.error(format!(
598                        "iconst produces an integer and this one produces {}",
599                        res(0)
600                    ));
601                }
602            }
603            Opcode::FConst => {
604                if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_float() {
605                    self.error(format!(
606                        "fconst produces a floating point value and this one produces {}",
607                        res(0)
608                    ));
609                }
610            }
611            Opcode::Splat => {
612                if self.takes(opcode, arity, 0) && results == 1 && !res(0).is_vector() {
613                    self.error(format!("splat produces a vector and this one produces {}", res(0)));
614                }
615            }
616            Opcode::GlobalAddr
617            | Opcode::StackSave
618            | Opcode::FrameAddress
619            | Opcode::ReturnAddress => {
620                if results == 1 && !res(0).is_ptr() {
621                    self.error(format!(
622                        "{} produces a pointer and this one produces {}",
623                        opcode.name(),
624                        res(0)
625                    ));
626                }
627            }
628
629            // Integer arithmetic: two of one type, and that type back.
630            Opcode::Add
631            | Opcode::Sub
632            | Opcode::Mul
633            | Opcode::SDiv
634            | Opcode::UDiv
635            | Opcode::SRem
636            | Opcode::URem
637            | Opcode::And
638            | Opcode::Or
639            | Opcode::Xor
640            | Opcode::Shl
641            | Opcode::LShr
642            | Opcode::AShr => {
643                if self.takes(opcode, arity, 2) {
644                    self.integer(opcode, arg(0), 0);
645                    self.agree(opcode, arg(0), arg(1));
646                    if results == 1 {
647                        self.produces(opcode, res(0), arg(0));
648                    }
649                }
650            }
651
652            // Floating point arithmetic, the same shape with one more operand for `fma`.
653            Opcode::FAdd | Opcode::FSub | Opcode::FMul | Opcode::FDiv | Opcode::FRem => {
654                if self.takes(opcode, arity, 2) {
655                    self.floating(opcode, arg(0), 0);
656                    self.agree(opcode, arg(0), arg(1));
657                    if results == 1 {
658                        self.produces(opcode, res(0), arg(0));
659                    }
660                }
661            }
662            Opcode::FNeg => {
663                if self.takes(opcode, arity, 1) {
664                    self.floating(opcode, arg(0), 0);
665                    if results == 1 {
666                        self.produces(opcode, res(0), arg(0));
667                    }
668                }
669            }
670            Opcode::Fma => {
671                if self.takes(opcode, arity, 3) {
672                    self.floating(opcode, arg(0), 0);
673                    self.agree(opcode, arg(0), arg(1));
674                    self.agree(opcode, arg(0), arg(2));
675                    if results == 1 {
676                        self.produces(opcode, res(0), arg(0));
677                    }
678                }
679            }
680
681            // A comparison answers one bit per lane, whatever it compared.
682            Opcode::ICmp | Opcode::FCmp => {
683                if self.takes(opcode, arity, 2) {
684                    if opcode == Opcode::FCmp {
685                        self.floating(opcode, arg(0), 0);
686                    } else if !arg(0).lane().is_int() && !arg(0).is_ptr() {
687                        self.error(format!(
688                            "operand 1 of icmp is an integer or a pointer and this one is {}",
689                            arg(0)
690                        ));
691                    }
692                    self.agree(opcode, arg(0), arg(1));
693                    if results == 1 {
694                        self.produces(opcode, res(0), arg(0).with_lane(Type::I1));
695                    }
696                }
697            }
698
699            // Conversions, each of which says which way it goes and by how much.
700            Opcode::Trunc | Opcode::SExt | Opcode::ZExt => {
701                if self.takes(opcode, arity, 1) && results == 1 {
702                    self.integer(opcode, arg(0), 0);
703                    self.lanes(opcode, res(0), arg(0));
704                    self.widens(opcode, res(0), arg(0), opcode != Opcode::Trunc);
705                }
706            }
707            Opcode::FPTrunc | Opcode::FPExt => {
708                if self.takes(opcode, arity, 1) && results == 1 {
709                    self.floating(opcode, arg(0), 0);
710                    self.lanes(opcode, res(0), arg(0));
711                    self.widens(opcode, res(0), arg(0), opcode == Opcode::FPExt);
712                }
713            }
714            Opcode::FPToSI | Opcode::FPToUI => {
715                if self.takes(opcode, arity, 1) && results == 1 {
716                    self.floating(opcode, arg(0), 0);
717                    self.lanes(opcode, res(0), arg(0));
718                    if !res(0).lane().is_int() {
719                        self.error(format!(
720                            "{} produces an integer and this one produces {}",
721                            opcode.name(),
722                            res(0)
723                        ));
724                    }
725                }
726            }
727            Opcode::SIToFP | Opcode::UIToFP => {
728                if self.takes(opcode, arity, 1) && results == 1 {
729                    self.integer(opcode, arg(0), 0);
730                    self.lanes(opcode, res(0), arg(0));
731                    if !res(0).lane().is_float() {
732                        self.error(format!(
733                            "{} produces a floating point value and this one produces {}",
734                            opcode.name(),
735                            res(0)
736                        ));
737                    }
738                }
739            }
740            Opcode::PtrToInt => {
741                if self.takes(opcode, arity, 1) && results == 1 {
742                    self.pointer(opcode, arg(0), 0);
743                    self.integer(opcode, res(0), 0);
744                }
745            }
746            Opcode::IntToPtr => {
747                if self.takes(opcode, arity, 1) && results == 1 {
748                    self.integer(opcode, arg(0), 0);
749                    if !res(0).is_ptr() {
750                        self.error(format!(
751                            "inttoptr produces a pointer and this one produces {}",
752                            res(0)
753                        ));
754                    }
755                }
756            }
757            Opcode::Bitcast => {
758                if self.takes(opcode, arity, 1) && results == 1 {
759                    let (from, to) = (arg(0), res(0));
760                    if from.is_ptr() != to.is_ptr() {
761                        // Between an address and a number there is a conversion of its own,
762                        // and using this one would hide it from anything looking for one.
763                        self.error(
764                            "a bitcast between a pointer and a number is ptrtoint or inttoptr",
765                        );
766                    } else if width(from) != width(to) {
767                        self.error(format!("a bitcast keeps the width and {from} and {to} differ"));
768                    }
769                }
770            }
771
772            // Memory.
773            Opcode::Alloca => {
774                if arity > 1 {
775                    self.takes(opcode, arity, 1);
776                } else if arity == 1 {
777                    self.integer(opcode, arg(0), 0);
778                } else if func.block_of(inst) != func.entry() {
779                    // One of a fixed size in a loop is a stack that grows every time round,
780                    // which is what the dynamic form asks for explicitly.
781                    self.error("an alloca of a fixed size belongs in the entry block");
782                }
783                if results == 1 && !res(0).is_ptr() {
784                    self.error(format!(
785                        "alloca produces a pointer and this one produces {}",
786                        res(0)
787                    ));
788                }
789            }
790            Opcode::Load | Opcode::AtomicLoad => {
791                if self.takes(opcode, arity, 1) {
792                    self.pointer(opcode, arg(0), 0);
793                }
794                if results == 1 && res(0).is_void() {
795                    self.error(format!("{} reads a value and void is not one", opcode.name()));
796                }
797            }
798            Opcode::Store | Opcode::AtomicStore => {
799                if self.takes(opcode, arity, 2) {
800                    if arg(0).is_void() {
801                        self.error(format!("{} writes a value and void is not one", opcode.name()));
802                    }
803                    self.pointer(opcode, arg(1), 1);
804                }
805            }
806            Opcode::PtrAdd => {
807                if self.takes(opcode, arity, 2) {
808                    self.pointer(opcode, arg(0), 0);
809                    self.integer(opcode, arg(1), 1);
810                    if results == 1 && !res(0).is_ptr() {
811                        self.error(format!(
812                            "ptr_add produces a pointer and this one produces {}",
813                            res(0)
814                        ));
815                    }
816                }
817            }
818            Opcode::Memcpy | Opcode::Memmove => {
819                if self.takes(opcode, arity, 2) {
820                    self.pointer(opcode, arg(0), 0);
821                    self.pointer(opcode, arg(1), 1);
822                }
823            }
824            Opcode::Memset => {
825                if self.takes(opcode, arity, 2) {
826                    self.pointer(opcode, arg(0), 0);
827                    self.integer(opcode, arg(1), 1);
828                }
829            }
830            Opcode::AtomicRmw => {
831                if self.takes(opcode, arity, 2) {
832                    self.pointer(opcode, arg(0), 0);
833                    self.integer(opcode, arg(1), 1);
834                    if results == 1 {
835                        self.produces(opcode, res(0), arg(1));
836                    }
837                }
838            }
839            Opcode::Cmpxchg => {
840                if self.takes(opcode, arity, 3) {
841                    self.pointer(opcode, arg(0), 0);
842                    self.agree(opcode, arg(1), arg(2));
843                    if results == 2 {
844                        self.produces(opcode, res(0), arg(1));
845                        self.produces(opcode, res(1), arg(1).with_lane(Type::I1));
846                    }
847                }
848            }
849            Opcode::Fence | Opcode::Unreachable | Opcode::UnreachableHint => {
850                self.takes(opcode, arity, 0);
851            }
852            Opcode::Prefetch | Opcode::StackRestore | Opcode::VaStart | Opcode::VaEnd => {
853                if self.takes(opcode, arity, 1) {
854                    self.pointer(opcode, arg(0), 0);
855                }
856            }
857            Opcode::VaCopy => {
858                if self.takes(opcode, arity, 2) {
859                    self.pointer(opcode, arg(0), 0);
860                    self.pointer(opcode, arg(1), 1);
861                }
862            }
863            Opcode::VaArg => {
864                if self.takes(opcode, arity, 1) {
865                    self.pointer(opcode, arg(0), 0);
866                }
867                if results == 1 && res(0).is_void() {
868                    self.error("va_arg reads a value and void is not one");
869                }
870            }
871
872            // Control.
873            Opcode::Jump => {
874                self.takes(opcode, arity, 0);
875                self.targets(func, inst, 1);
876            }
877            Opcode::BrIf => {
878                if self.takes(opcode, arity, 1) && arg(0) != Type::I1 {
879                    self.error(format!("br_if branches on an i1 and this one on {}", arg(0)));
880                }
881                self.targets(func, inst, 2);
882            }
883            Opcode::Switch => {
884                if self.takes(opcode, arity, 1) {
885                    self.integer(opcode, arg(0), 0);
886                }
887            }
888            Opcode::BlockAddr => {
889                self.takes(opcode, arity, 0);
890                self.targets(func, inst, 1);
891                if results == 1 && !res(0).is_ptr() {
892                    self.error(format!(
893                        "block_addr produces a pointer and this one produces {}",
894                        res(0)
895                    ));
896                }
897                if func.successors(inst).any(|call| !func[call.args].is_empty()) {
898                    // Taking the address is not arriving, so there is nothing to hand over.
899                    // What the block takes is passed by the branch that goes there.
900                    self.error("block_addr names a block and passes it arguments");
901                }
902            }
903            Opcode::IndirectBr => {
904                if self.takes(opcode, arity, 1) {
905                    self.pointer(opcode, arg(0), 0);
906                }
907                // No count to check: how many blocks one of these can arrive at is how many
908                // the front end says, and a `goto *p` in a function with one label has one.
909            }
910            Opcode::Return => {
911                let want = &func.signature().returns;
912                if arity != want.len() {
913                    self.error(format!(
914                        "the signature returns {} and this returns {arity}",
915                        want.len()
916                    ));
917                } else {
918                    for (index, ty) in want.iter().map(|param| param.ty).enumerate() {
919                        if arg(index) != ty {
920                            self.error(format!(
921                                "result {} of the signature is {ty} and this returns {}",
922                                index + 1,
923                                arg(index)
924                            ));
925                        }
926                    }
927                }
928            }
929
930            // Calls, whose signature is what says the shape.
931            Opcode::Call | Opcode::CallIndirect | Opcode::TailCall => {
932                let Extra::Call(at) = data.extra else { return };
933                let info = func[at];
934                let signature = &func[info.signature];
935                let indirect = usize::from(opcode == Opcode::CallIndirect);
936                if indirect == 1 {
937                    if arity == 0 {
938                        self.error("call_indirect calls through a pointer and has no operands");
939                        return;
940                    }
941                    self.pointer(opcode, arg(0), 0);
942                }
943                let passed = arity - indirect;
944                let enough = if signature.variadic {
945                    passed >= signature.params.len()
946                } else {
947                    passed == signature.params.len()
948                };
949                if enough {
950                    for (index, ty) in signature.param_types().enumerate() {
951                        if arg(index + indirect) != ty {
952                            self.error(format!(
953                                "parameter {} of the signature is {ty} and this argument is {}",
954                                index + 1,
955                                arg(index + indirect)
956                            ));
957                        }
958                    }
959                } else {
960                    self.error(format!(
961                        "the signature takes {}{} and this call passes {passed}",
962                        signature.params.len(),
963                        if signature.variadic { " or more" } else { "" }
964                    ));
965                }
966                if results != signature.returns.len() {
967                    self.error(format!(
968                        "the signature returns {} and this call produces {results}",
969                        signature.returns.len()
970                    ));
971                } else {
972                    for (index, ty) in signature.return_types().enumerate() {
973                        if res(index) != ty {
974                            self.error(format!(
975                                "result {} of the signature is {ty} and this call produces {}",
976                                index + 1,
977                                res(index)
978                            ));
979                        }
980                    }
981                }
982                // Where the callee is in this module, the signature at the call and the one at
983                // the function are the same signature or one of them is wrong.
984                if let Some(callee) = info.callee {
985                    if let Some(SymbolRef::Func(id)) = self.module.lookup(callee) {
986                        if self.module[id].signature() != signature {
987                            self.error(format!(
988                                "@{} is declared here with another signature",
989                                self.names.resolve(callee)
990                            ));
991                        }
992                    }
993                }
994            }
995
996            // Bit counting, which answers in the type it was asked about.
997            Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop | Opcode::Bswap | Opcode::Bitreverse => {
998                if self.takes(opcode, arity, 1) {
999                    self.integer(opcode, arg(0), 0);
1000                    if results == 1 {
1001                        self.produces(opcode, res(0), arg(0));
1002                    }
1003                }
1004            }
1005
1006            // The overflow-checked forms, which answer the value and whether it wrapped.
1007            Opcode::SAddOverflow
1008            | Opcode::UAddOverflow
1009            | Opcode::SSubOverflow
1010            | Opcode::USubOverflow
1011            | Opcode::SMulOverflow
1012            | Opcode::UMulOverflow => {
1013                if self.takes(opcode, arity, 2) {
1014                    self.integer(opcode, arg(0), 0);
1015                    self.agree(opcode, arg(0), arg(1));
1016                    if results == 2 {
1017                        self.produces(opcode, res(0), arg(0));
1018                        self.produces(opcode, res(1), arg(0).with_lane(Type::I1));
1019                    }
1020                }
1021            }
1022            Opcode::Expect => {
1023                if self.takes(opcode, arity, 2) {
1024                    self.agree(opcode, arg(0), arg(1));
1025                    if results == 1 {
1026                        self.produces(opcode, res(0), arg(0));
1027                    }
1028                }
1029            }
1030
1031            // What is left says nothing about its operands here: the two markers are placed by
1032            // the front end around code the optimizer must not move, inline assembly is
1033            // whatever its constraints say, and a target intrinsic is the target's own rule.
1034            Opcode::SetjmpMarker
1035            | Opcode::LongjmpMarker
1036            | Opcode::InlineAsm
1037            | Opcode::TargetIntrinsic => {}
1038        }
1039    }
1040
1041    // The small questions the shapes are made of.
1042
1043    /// Reports the operand count when it is wrong, and answers whether it was right.
1044    fn takes(&mut self, opcode: Opcode, got: usize, want: usize) -> bool {
1045        if got == want {
1046            return true;
1047        }
1048        self.error(format!("{} takes {want} operands and this one has {got}", opcode.name()));
1049        false
1050    }
1051
1052    /// Reports the number of branch targets when it is wrong.
1053    fn targets(&mut self, func: &'a Func, inst: Inst, want: usize) {
1054        let got = func.successors(inst).count();
1055        if got != want {
1056            self.error(format!(
1057                "{} branches to {want} blocks and this one to {got}",
1058                func[inst].opcode.name()
1059            ));
1060        }
1061    }
1062
1063    fn integer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1064        if !ty.lane().is_int() {
1065            self.error(format!(
1066                "operand {} of {} is an integer and this one is {ty}",
1067                n + 1,
1068                opcode.name()
1069            ));
1070        }
1071    }
1072
1073    fn floating(&mut self, opcode: Opcode, ty: Type, n: usize) {
1074        if !ty.lane().is_float() {
1075            self.error(format!(
1076                "operand {} of {} is a floating point value and this one is {ty}",
1077                n + 1,
1078                opcode.name()
1079            ));
1080        }
1081    }
1082
1083    fn pointer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1084        if !ty.is_ptr() {
1085            self.error(format!(
1086                "operand {} of {} is a pointer and this one is {ty}",
1087                n + 1,
1088                opcode.name()
1089            ));
1090        }
1091    }
1092
1093    /// Two operands that have to be the same type as each other.
1094    fn agree(&mut self, opcode: Opcode, first: Type, second: Type) {
1095        if first != second {
1096            self.error(format!(
1097                "the operands of {} have one type and these are {first} and {second}",
1098                opcode.name()
1099            ));
1100        }
1101    }
1102
1103    /// A result that has to be a particular type.
1104    fn produces(&mut self, opcode: Opcode, got: Type, want: Type) {
1105        if got != want {
1106            self.error(format!(
1107                "{} produces {want} here and this one produces {got}",
1108                opcode.name()
1109            ));
1110        }
1111    }
1112
1113    /// A conversion that keeps the lane count, since none of them changes it.
1114    fn lanes(&mut self, opcode: Opcode, to: Type, from: Type) {
1115        if to.lanes() != from.lanes() {
1116            self.error(format!(
1117                "{} keeps the lane count and {from} has {} and {to} has {}",
1118                opcode.name(),
1119                from.lanes(),
1120                to.lanes()
1121            ));
1122        }
1123    }
1124
1125    /// A conversion that has to go the way its name says.
1126    fn widens(&mut self, opcode: Opcode, to: Type, from: Type, wider: bool) {
1127        let (a, b) = (to.lane().bits(), from.lane().bits());
1128        let ok = if wider { a > b } else { a < b };
1129        if !ok {
1130            let way = if wider { "wider" } else { "narrower" };
1131            self.error(format!(
1132                "{} produces something {way} and {from} to {to} is not",
1133                opcode.name()
1134            ));
1135        }
1136    }
1137
1138    // Reporting.
1139
1140    fn error(&mut self, message: impl Into<String>) {
1141        let at = self.locate();
1142        self.at(at, message);
1143    }
1144
1145    fn at(&mut self, at: String, message: impl Into<String>) {
1146        self.errors.push(VerifyError { at, message: message.into() });
1147    }
1148
1149    /// Where the walk is, as text.
1150    ///
1151    /// A block is named by its index rather than by the number the printer would give it,
1152    /// which is the same number for a module that came from the parser and can differ for one
1153    /// a pass has been reordering.
1154    fn locate(&self) -> String {
1155        use fmt::Write as _;
1156        let mut at = String::new();
1157        if let Some(func) = self.func {
1158            let _ = write!(at, "@{}", self.names.resolve(func.name));
1159            if let Some(block) = self.block {
1160                let _ = write!(at, " block{}", block.raw());
1161            }
1162            if let Some(inst) = self.inst {
1163                let _ = write!(at, " {}", func[inst].opcode.name());
1164            }
1165        }
1166        at
1167    }
1168}
1169
1170/// Where each instruction is, so that a use in the same block as its definition can be told
1171/// from a use before it.
1172struct Layout {
1173    block: Vec<Option<Block>>,
1174    position: Vec<u32>,
1175}
1176
1177impl Layout {
1178    fn new(func: &Func) -> Self {
1179        let counts = func.counts();
1180        let mut layout =
1181            Layout { block: vec![None; counts.insts], position: vec![0; counts.insts] };
1182        for block in func.blocks() {
1183            for (position, inst) in func.insts(block).enumerate() {
1184                layout.block[inst.index()] = Some(block);
1185                layout.position[inst.index()] = position as u32;
1186            }
1187        }
1188        layout
1189    }
1190
1191    fn block_of(&self, inst: Inst) -> Option<Block> {
1192        self.block[inst.index()]
1193    }
1194
1195    fn position(&self, inst: Inst) -> u32 {
1196        self.position[inst.index()]
1197    }
1198}
1199
1200/// Which block dominates which, by the iterative algorithm of Cooper, Harvey and Kennedy,
1201/// "A Simple, Fast Dominance Algorithm" (2001).
1202///
1203/// The one in the verifier rather than a shared analysis, because the optimizer's dominator
1204/// tree is incrementally maintained across a pass and this one is built from nothing every time
1205/// it is asked for. The two want different things from the same idea.
1206struct Doms {
1207    /// Where each block is in reverse postorder, which is the order the fixed point converges
1208    /// fastest in, and `None` for one the entry does not reach.
1209    rank: Vec<Option<u32>>,
1210    /// The rank of each block's immediate dominator, indexed by rank.
1211    idom: Vec<u32>,
1212}
1213
1214impl Doms {
1215    fn new(func: &Func) -> Self {
1216        let counts = func.counts();
1217        let entry = func.entry().expect("a function with blocks has a first one");
1218
1219        // Every instruction that names a block, and not only the terminator. The one other
1220        // instruction that names one is `block_addr`, whose block is somewhere an
1221        // `indirect_br` can arrive at from anywhere the address reaches, so counting it as an
1222        // edge is what keeps a label that is only jumped to indirectly out of the reachability
1223        // report. The edge is a real one in the only direction that matters here: it can add
1224        // predecessors to a block and so take dominators away from it, which makes the check
1225        // on the uses stricter and never looser.
1226        let mut succs: Vec<Vec<Block>> = vec![Vec::new(); counts.blocks];
1227        for block in func.blocks() {
1228            for inst in func.insts(block) {
1229                succs[block.index()].extend(func.successors(inst).map(|call| call.block));
1230            }
1231        }
1232
1233        // Postorder by an explicit stack, since a chain of blocks can be as long as the
1234        // function is and the recursive form would run out of stack on one.
1235        let mut order = Vec::new();
1236        let mut seen = vec![false; counts.blocks];
1237        let mut stack = vec![(entry, 0usize)];
1238        seen[entry.index()] = true;
1239        while let Some((block, next)) = stack.pop() {
1240            match succs[block.index()].get(next) {
1241                Some(&target) => {
1242                    stack.push((block, next + 1));
1243                    if !seen[target.index()] {
1244                        seen[target.index()] = true;
1245                        stack.push((target, 0));
1246                    }
1247                }
1248                None => order.push(block),
1249            }
1250        }
1251        order.reverse();
1252
1253        let mut rank = vec![None; counts.blocks];
1254        for (index, &block) in order.iter().enumerate() {
1255            rank[block.index()] = Some(index as u32);
1256        }
1257        let mut preds: Vec<Vec<u32>> = vec![Vec::new(); order.len()];
1258        for (index, &block) in order.iter().enumerate() {
1259            for &target in &succs[block.index()] {
1260                if let Some(target) = rank[target.index()] {
1261                    preds[target as usize].push(index as u32);
1262                }
1263            }
1264        }
1265
1266        // The entry dominates itself, and everything else starts undefined, which is what the
1267        // sentinel is. The fixed point is reached in one pass over a reducible CFG and in two
1268        // over the ones a `goto` produces.
1269        const NONE: u32 = u32::MAX;
1270        let mut idom = vec![NONE; order.len()];
1271        if !order.is_empty() {
1272            idom[0] = 0;
1273        }
1274        let mut changed = true;
1275        while changed {
1276            changed = false;
1277            for index in 1..order.len() {
1278                let mut new = NONE;
1279                for &pred in &preds[index] {
1280                    if idom[pred as usize] == NONE {
1281                        continue;
1282                    }
1283                    new = if new == NONE { pred } else { meet(&idom, new, pred) };
1284                }
1285                if new != NONE && idom[index] != new {
1286                    idom[index] = new;
1287                    changed = true;
1288                }
1289            }
1290        }
1291        Doms { rank, idom }
1292    }
1293
1294    /// Whether the entry block reaches this one at all.
1295    fn reaches(&self, block: Block) -> bool {
1296        self.rank[block.index()].is_some()
1297    }
1298
1299    /// Whether every path from the entry to `block` goes through `of`.
1300    ///
1301    /// A block the entry does not reach is dominated by everything, which is vacuously true and
1302    /// keeps an unreachable block from being reported twice over, once for being unreachable
1303    /// and once for every value it uses.
1304    fn dominates(&self, of: Block, block: Block) -> bool {
1305        let (Some(a), Some(b)) = (self.rank[of.index()], self.rank[block.index()]) else {
1306            return true;
1307        };
1308        let mut walk = b;
1309        while walk > a {
1310            walk = self.idom[walk as usize];
1311        }
1312        walk == a
1313    }
1314}
1315
1316/// The nearest block that dominates both, walking the two chains towards the entry.
1317fn meet(idom: &[u32], mut a: u32, mut b: u32) -> u32 {
1318    while a != b {
1319        while a > b {
1320            a = idom[a as usize];
1321        }
1322        while b > a {
1323            b = idom[b as usize];
1324        }
1325    }
1326    a
1327}
1328
1329/// How many bits a value of that type occupies, counting every lane.
1330fn width(ty: Type) -> u64 {
1331    u64::from(ty.bits()) * u64::from(ty.lanes())
1332}
1333
1334/// A list of types, as the text writes them.
1335fn types(list: &[Type]) -> String {
1336    if list.is_empty() {
1337        return "nothing".to_string();
1338    }
1339    list.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
1340}
1341
1342#[cfg(test)]
1343mod tests {
1344    use rucc_base::{Idx, Interner, Symbol};
1345    use rucc_diag::Span;
1346    use rucc_target::{Arch, Env, Os, Triple};
1347
1348    use super::*;
1349    use crate::fixtures::{EXAMPLE, SYMBOLS, ZOO};
1350    use crate::func::Builder;
1351    use crate::inst::{InstData, MetaNode, Signature};
1352    use crate::{Flags, IntPred, parse};
1353
1354    fn target() -> TargetInfo {
1355        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1356    }
1357
1358    /// Everything wrong with a module written as text, which is how most of these are written:
1359    /// the parser turns down what is malformed and what is left is what the verifier is for.
1360    fn errors(text: &str) -> Vec<String> {
1361        let mut names = Interner::new();
1362        let module = match parse(text, &mut names) {
1363            Ok(module) => module,
1364            Err(error) => panic!("{error}"),
1365        };
1366        match verify(&module, &names) {
1367            Ok(()) => Vec::new(),
1368            Err(errors) => errors.iter().map(ToString::to_string).collect(),
1369        }
1370    }
1371
1372    /// The one thing wrong with it.
1373    fn only(text: &str) -> String {
1374        let found = errors(text);
1375        assert_eq!(found.len(), 1, "{found:#?}");
1376        found.into_iter().next().expect("just counted one")
1377    }
1378
1379    const HEADER: &str = "\
1380; ModuleID = 'bad.c'
1381; format 0
1382target triple = \"x86_64-unknown-linux-gnu\"
1383target datalayout = \"e-p:64:64-i64:64-f80:128-S128\"
1384";
1385
1386    /// A function around that body, with that signature.
1387    fn wrap(signature: &str, body: &str) -> String {
1388        format!("{HEADER}\nfunc @f{signature}, linkage(external) {{\n{body}}}\n")
1389    }
1390
1391    #[test]
1392    fn the_three_fixtures_are_modules_the_compiler_may_believe() {
1393        for text in [EXAMPLE, ZOO, SYMBOLS] {
1394            assert_eq!(errors(text), Vec::<String>::new());
1395        }
1396    }
1397
1398    #[test]
1399    fn a_use_that_its_definition_does_not_reach_is_reported() {
1400        let text = wrap(
1401            "(i1) -> i32",
1402            "block0(%0: i1):
1403    br_if %0, block1, block2
1404
1405block1:
1406    %1 = iconst.i32 7
1407    jump block2
1408
1409block2:
1410    return %1
1411",
1412        );
1413        assert_eq!(
1414            only(&text),
1415            "@f block2 return: %1 is produced in block1 and does not reach here"
1416        );
1417    }
1418
1419    #[test]
1420    fn a_use_before_its_definition_in_the_same_block_is_reported() {
1421        let text = wrap(
1422            "() -> i32",
1423            "block0:
1424    %0 = iconst.i32 1
1425    %1 = add %2, %0
1426    %2 = iconst.i32 2
1427    return %1
1428",
1429        );
1430        assert_eq!(only(&text), "@f block0 add: %2 is produced in block0 and does not reach here");
1431    }
1432
1433    #[test]
1434    fn a_call_that_takes_its_arguments_the_way_the_abi_says_is_believed() {
1435        let text = wrap(
1436            "(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext)",
1437            "block0(%0: ptr, %1: ptr, %2: i8):
1438    return
1439",
1440        );
1441        assert_eq!(errors(&text), Vec::<String>::new());
1442    }
1443
1444    #[test]
1445    fn an_sret_that_is_not_the_first_parameter_is_reported() {
1446        let text = wrap(
1447            "(ptr byval(8, align 8), ptr sret(16, align 8))",
1448            "block0(%0: ptr, %1: ptr):
1449    return
1450",
1451        );
1452        assert_eq!(only(&text), "@f: sret is the first parameter and this one is not");
1453    }
1454
1455    #[test]
1456    fn a_function_returning_through_sret_returns_nothing_else() {
1457        let text = wrap(
1458            "(ptr sret(8, align 8)) -> i32",
1459            "block0(%0: ptr):
1460    %1 = iconst.i32 7
1461    return %1
1462",
1463        );
1464        assert_eq!(only(&text), "@f: a signature returning through sret returns nothing else");
1465    }
1466
1467    #[test]
1468    fn an_object_that_travels_indirectly_travels_behind_a_pointer() {
1469        let text = wrap(
1470            "(i32 byval(4, align 4))",
1471            "block0(%0: i32):
1472    return
1473",
1474        );
1475        assert_eq!(only(&text), "@f: parameter 1 travels indirectly and i32 is not a pointer");
1476    }
1477
1478    #[test]
1479    fn an_alignment_a_parameter_could_not_have_is_reported() {
1480        let text = wrap(
1481            "(ptr byval(24, align 3))",
1482            "block0(%0: ptr):
1483    return
1484",
1485        );
1486        assert_eq!(only(&text), "@f: an alignment is a power of two and this is 3");
1487    }
1488
1489    #[test]
1490    fn a_result_does_not_travel_indirectly() {
1491        // A return value too large for the registers comes back through a parameter, so this
1492        // says an ABI nothing implements.
1493        let text = wrap(
1494            "() -> ptr byval(16, align 8)",
1495            "block0:
1496    %0 = iconst.i64 0
1497    %1 = inttoptr.ptr %0
1498    return %1
1499",
1500        );
1501        assert_eq!(only(&text), "@f: result 1 travels indirectly and a result cannot");
1502    }
1503
1504    #[test]
1505    fn an_extension_is_asked_of_an_integer_and_not_of_anything_else() {
1506        let text = wrap(
1507            "(ptr zext)",
1508            "block0(%0: ptr):
1509    return
1510",
1511        );
1512        assert_eq!(only(&text), "@f: parameter 1 is extended and ptr is not an integer");
1513    }
1514
1515    #[test]
1516    fn a_branch_that_passes_the_wrong_number_of_arguments_is_reported() {
1517        let text = wrap(
1518            "(i32)",
1519            "block0(%0: i32):
1520    jump block1(%0)
1521
1522block1:
1523    return
1524",
1525        );
1526        assert_eq!(
1527            only(&text),
1528            "@f block0 jump: block1 takes 0 arguments and this branch passes 1"
1529        );
1530    }
1531
1532    #[test]
1533    fn a_branch_that_passes_the_wrong_type_is_reported() {
1534        let text = wrap(
1535            "(i32) -> i32",
1536            "block0(%0: i32):
1537    %1 = sext.i64 %0
1538    jump block1(%1)
1539
1540block1(%2: i32):
1541    return %2
1542",
1543        );
1544        assert_eq!(only(&text), "@f block0 jump: argument 1 to block1 is i32 and this one is i64");
1545    }
1546
1547    #[test]
1548    fn a_block_that_does_not_end_in_a_terminator_is_reported() {
1549        let text = wrap("()", "block0:\n    %0 = iconst.i32 1\n");
1550        assert_eq!(only(&text), "@f block0: this block does not end in a terminator");
1551    }
1552
1553    #[test]
1554    fn an_instruction_after_the_terminator_is_reported() {
1555        let text = wrap("()", "block0:\n    return\n    %0 = iconst.i32 1\n");
1556        assert_eq!(only(&text), "@f block0 iconst: this comes after the block's terminator");
1557    }
1558
1559    #[test]
1560    fn an_unreachable_block_is_reported() {
1561        let text = wrap("()", "block0:\n    return\n\nblock1:\n    return\n");
1562        assert_eq!(only(&text), "@f block1: this block is not reachable and has not been deleted");
1563    }
1564
1565    #[test]
1566    fn a_block_a_jump_to_an_address_arrives_at_is_an_ordinary_target() {
1567        let text = wrap(
1568            "(ptr) -> i32",
1569            "block0(%0: ptr):
1570    %1 = block_addr block1
1571    indirect_br %0, block1
1572
1573block1:
1574    %2 = iconst.i32 1
1575    return %2
1576",
1577        );
1578        assert_eq!(errors(&text), Vec::<String>::new());
1579    }
1580
1581    #[test]
1582    fn a_block_whose_address_is_taken_is_reached_by_the_taking_of_it() {
1583        // Nothing branches to block1 here and its address has left the function, so the one
1584        // thing that says it is still somewhere control can arrive at is the `block_addr`.
1585        let text = wrap(
1586            "() -> ptr",
1587            "block0:
1588    %0 = block_addr block1
1589    return %0
1590
1591block1:
1592    unreachable
1593",
1594        );
1595        assert_eq!(errors(&text), Vec::<String>::new());
1596    }
1597
1598    #[test]
1599    fn taking_the_address_of_a_block_and_passing_it_arguments_is_reported() {
1600        // Taking the address is not arriving, so there is nothing to hand over. What block1
1601        // takes is passed by whatever branches there.
1602        let text = wrap(
1603            "(i32) -> ptr",
1604            "block0(%0: i32):
1605    %1 = block_addr block1(%0)
1606    return %1
1607
1608block1(%2: i32):
1609    unreachable
1610",
1611        );
1612        assert_eq!(
1613            only(&text),
1614            "@f block0 block_addr: block_addr names a block and passes it arguments"
1615        );
1616    }
1617
1618    #[test]
1619    fn a_jump_to_something_that_is_not_an_address_is_reported() {
1620        let text = wrap(
1621            "(i32)",
1622            "block0(%0: i32):
1623    indirect_br %0, block1
1624
1625block1:
1626    return
1627",
1628        );
1629        assert_eq!(
1630            only(&text),
1631            "@f block0 indirect_br: operand 1 of indirect_br is a pointer and this one is i32"
1632        );
1633    }
1634
1635    #[test]
1636    fn a_branch_back_to_the_entry_block_is_reported() {
1637        // The entry block's parameters are the function's arguments, so a branch to it would be
1638        // a second place they arrive from.
1639        let text = wrap(
1640            "(i32)",
1641            "block0(%0: i32):
1642    jump block1
1643
1644block1:
1645    jump block0(%0)
1646",
1647        );
1648        assert_eq!(
1649            only(&text),
1650            "@f block0: the entry block is branched to, and it takes the arguments"
1651        );
1652    }
1653
1654    #[test]
1655    fn an_entry_block_that_does_not_take_the_arguments_is_reported() {
1656        let text = wrap("(i32)", "block0(%0: i64):\n    return\n");
1657        assert_eq!(only(&text), "@f: the entry block takes i64 and the signature says i32");
1658    }
1659
1660    #[test]
1661    fn a_flag_the_opcode_does_not_read_is_reported() {
1662        let text =
1663            wrap("(i32) -> i32", "block0(%0: i32):\n    %1 = add.exact %0, %0\n    return %1\n");
1664        assert_eq!(only(&text), "@f block0 add: add does not read `exact`");
1665    }
1666
1667    #[test]
1668    fn an_ordering_the_operation_cannot_be_asked_for_is_reported() {
1669        let text = wrap(
1670            "(ptr) -> i32",
1671            "block0(%0: ptr):\n    %1 = atomic_load.i32 %0, align 4, release\n    return %1\n",
1672        );
1673        assert_eq!(only(&text), "@f block0 atomic_load: atomic_load cannot be asked for release");
1674    }
1675
1676    #[test]
1677    fn an_ordering_on_the_non_atomic_form_is_reported() {
1678        let text = wrap(
1679            "(ptr) -> i32",
1680            "block0(%0: ptr):\n    %1 = load.i32 %0, align 4, acquire\n    return %1\n",
1681        );
1682        assert_eq!(only(&text), "@f block0 load: load cannot be asked for acquire");
1683    }
1684
1685    #[test]
1686    fn an_alloca_of_a_fixed_size_outside_the_entry_block_is_reported() {
1687        let text = wrap(
1688            "()",
1689            "block0:
1690    jump block1
1691
1692block1:
1693    %0 = alloca, size 16, align 8
1694    return
1695",
1696        );
1697        assert_eq!(
1698            only(&text),
1699            "@f block1 alloca: an alloca of a fixed size belongs in the entry block"
1700        );
1701    }
1702
1703    #[test]
1704    fn a_dynamic_alloca_may_be_anywhere() {
1705        let text = wrap(
1706            "(i64)",
1707            "block0(%0: i64):
1708    jump block1
1709
1710block1:
1711    %1 = alloca %0, align 8
1712    return
1713",
1714        );
1715        assert_eq!(errors(&text), Vec::<String>::new());
1716    }
1717
1718    #[test]
1719    fn two_cases_of_a_switch_with_the_same_value_are_reported() {
1720        let text = wrap(
1721            "(i32)",
1722            "block0(%0: i32):
1723    switch %0, block1, [7 => block1, 7 => block1]
1724
1725block1:
1726    return
1727",
1728        );
1729        assert_eq!(only(&text), "@f block0 switch: two cases of this switch have the same value");
1730    }
1731
1732    #[test]
1733    fn operands_that_do_not_agree_are_reported() {
1734        let text = wrap(
1735            "(i32, i64) -> i32",
1736            "block0(%0: i32, %1: i64):\n    %2 = add %0, %1\n    return %2\n",
1737        );
1738        assert_eq!(
1739            only(&text),
1740            "@f block0 add: the operands of add have one type and these are i32 and i64"
1741        );
1742    }
1743
1744    #[test]
1745    fn a_conversion_that_goes_the_wrong_way_is_reported() {
1746        let text = wrap("(i32) -> i64", "block0(%0: i32):\n    %1 = trunc.i64 %0\n    return %1\n");
1747        assert_eq!(
1748            only(&text),
1749            "@f block0 trunc: trunc produces something narrower and i32 to i64 is not"
1750        );
1751    }
1752
1753    #[test]
1754    fn a_bitcast_between_an_address_and_a_number_is_reported() {
1755        let text =
1756            wrap("(ptr) -> i64", "block0(%0: ptr):\n    %1 = bitcast.i64 %0\n    return %1\n");
1757        assert_eq!(
1758            only(&text),
1759            "@f block0 bitcast: a bitcast between a pointer and a number is ptrtoint or inttoptr"
1760        );
1761    }
1762
1763    #[test]
1764    fn an_operand_of_the_wrong_kind_is_reported() {
1765        let text = wrap("(i32) -> i32", "block0(%0: i32):\n    %1 = fadd %0, %0\n    return %1\n");
1766        assert_eq!(
1767            only(&text),
1768            "@f block0 fadd: operand 1 of fadd is a floating point value and this one is i32"
1769        );
1770    }
1771
1772    #[test]
1773    fn a_condition_that_is_not_one_bit_is_reported() {
1774        let text = wrap(
1775            "(i32)",
1776            "block0(%0: i32):
1777    br_if %0, block1, block1
1778
1779block1:
1780    return
1781",
1782        );
1783        assert_eq!(only(&text), "@f block0 br_if: br_if branches on an i1 and this one on i32");
1784    }
1785
1786    #[test]
1787    fn a_return_that_does_not_match_the_signature_is_reported() {
1788        let text = wrap("() -> i32", "block0:\n    return\n");
1789        assert_eq!(only(&text), "@f block0 return: the signature returns 1 and this returns 0");
1790    }
1791
1792    #[test]
1793    fn a_call_that_disagrees_with_the_declaration_is_reported() {
1794        let text = format!(
1795            "{HEADER}
1796func @g(i32, ...) -> i32, linkage(external);
1797
1798func @f(i32) -> i32, linkage(external) {{
1799block0(%0: i32):
1800    %1 = call @g(%0) : (i32) -> i32
1801    return %1
1802}}
1803"
1804        );
1805        assert_eq!(only(&text), "@f block0 call: @g is declared here with another signature");
1806    }
1807
1808    #[test]
1809    fn a_global_whose_image_is_not_its_size_is_reported() {
1810        let text =
1811            format!("{HEADER}\nglobal @x : bytes 8 = {{ i32 7 }}, align 4, linkage(external)\n");
1812        assert_eq!(only(&text), "@x: the image is 4 bytes and the global is 8");
1813    }
1814
1815    #[test]
1816    fn a_pointer_in_an_image_has_no_width_and_is_reported() {
1817        // What a `NULL` in a static initializer used to become. A `ptr` takes the width of the
1818        // target's addresses and a type says nothing about the target, so the datum measured
1819        // zero bytes and the value it held went nowhere.
1820        let text = format!(
1821            "{HEADER}\nglobal @x : bytes 8 = {{ ptr 0x0, zero 8 }}, align 8, linkage(external)\n"
1822        );
1823        assert_eq!(only(&text), "@x: a scalar in an image has a width and ptr has none");
1824    }
1825
1826    #[test]
1827    fn a_declaration_of_something_another_module_defines_may_be_constant() {
1828        // `extern const int x;` names an object in the library's read only data. Whether it may
1829        // be written through is a fact about the object rather than about who holds the bytes.
1830        let text = format!("{HEADER}\nglobal @x : bytes 4, align 4, linkage(external), constant\n");
1831        assert!(errors(&text).is_empty(), "{:?}", errors(&text));
1832    }
1833
1834    #[test]
1835    fn an_alias_to_itself_is_reported() {
1836        let text = format!("{HEADER}\nalias @a = @a, linkage(external)\n");
1837        assert_eq!(only(&text), "@a: an alias to itself");
1838    }
1839
1840    #[test]
1841    fn an_ifunc_that_does_not_resolve_through_a_function_is_reported() {
1842        let text = format!(
1843            "{HEADER}
1844global @g : i32 = 0, align 4, linkage(external)
1845
1846ifunc @f = @g, linkage(external)
1847"
1848        );
1849        assert_eq!(
1850            only(&text),
1851            "@f: an ifunc resolves through a function and this target is not one"
1852        );
1853    }
1854
1855    #[test]
1856    fn attributes_that_contradict_each_other_are_reported() {
1857        let text =
1858            format!("{HEADER}\nfunc @f(), linkage(external), attrs(always_inline, noinline);\n");
1859        assert_eq!(
1860            only(&text),
1861            "@f: `always_inline` and `noinline` cannot both be true of a function"
1862        );
1863    }
1864
1865    // The rest are things no text can say, because the parser turns them down before the
1866    // verifier would see them. They are what a pass produces, which is who the verifier is for.
1867
1868    fn one_error(module: &Module, func: &Func, names: &Interner) -> String {
1869        match verify_func(module, func, names) {
1870            Ok(()) => panic!("that was expected to be turned down"),
1871            Err(errors) => {
1872                assert_eq!(errors.len(), 1, "{errors:#?}");
1873                errors[0].to_string()
1874            }
1875        }
1876    }
1877
1878    #[test]
1879    fn a_value_the_function_does_not_have_is_reported() {
1880        let mut names = Interner::new();
1881        let module = Module::new(names.intern("built.c"), &target());
1882        let mut func = Func::new(names.intern("f"), Signature::new());
1883        let block = func.create_block();
1884        let args = func.push_values(&[Value::from_usize(9)]);
1885        let inst =
1886            func.create_inst(InstData { args, ..InstData::new(Opcode::Return) }, &[], Span::DUMMY);
1887        func.append_inst(block, inst);
1888        assert_eq!(
1889            one_error(&module, &func, &names),
1890            "@f block0 return: %9 is not a value of this function"
1891        );
1892    }
1893
1894    #[test]
1895    fn a_value_whose_definition_has_been_taken_out_is_reported() {
1896        let mut names = Interner::new();
1897        let module = Module::new(names.intern("built.c"), &target());
1898        let i32_ = Type::int(32);
1899        let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
1900        let block = func.create_block();
1901        let mut b = Builder::new(&mut func, block);
1902        let value = b.iconst(i32_, 7);
1903        b.ret(&[value]);
1904        let Def::Result { inst, .. } = func[value].def else { unreachable!("a constant") };
1905        func.remove_inst(inst);
1906        assert_eq!(
1907            one_error(&module, &func, &names),
1908            "@f block0 return: %0 is produced by an instruction that is not in the function"
1909        );
1910    }
1911
1912    #[test]
1913    fn an_instruction_carrying_another_opcodes_payload_is_reported() {
1914        // This one prints as text the parser cannot read back, so the round trip would fail on
1915        // it somewhere else entirely if the verifier did not say so here.
1916        let mut names = Interner::new();
1917        let module = Module::new(names.intern("built.c"), &target());
1918        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1919        let block = func.create_block();
1920        let param = func.append_param(block, Type::int(32));
1921        let args = func.push_values(&[param, param]);
1922        let inst = func.create_inst(
1923            InstData { args, extra: Extra::IntPred(IntPred::Eq), ..InstData::new(Opcode::Add) },
1924            &[Type::int(32)],
1925            Span::DUMMY,
1926        );
1927        func.append_inst(block, inst);
1928        let ret = func.create_inst(InstData::new(Opcode::Return), &[], Span::DUMMY);
1929        func.append_inst(block, ret);
1930        assert_eq!(
1931            one_error(&module, &func, &names),
1932            "@f block0 add: add carries nothing and this one carries an integer comparison"
1933        );
1934    }
1935
1936    #[test]
1937    fn a_metadata_node_that_is_its_own_parent_is_reported() {
1938        let mut names = Interner::new();
1939        let mut module = Module::new(names.intern("built.c"), &target());
1940        module.add_meta(MetaNode {
1941            name: names.intern("int"),
1942            parent: Some(Idx::from_usize(0)),
1943            offset: 0,
1944        });
1945        let found = match verify(&module, &names) {
1946            Ok(()) => panic!("that was expected to be turned down"),
1947            Err(errors) => errors,
1948        };
1949        assert_eq!(found.len(), 1, "{found:#?}");
1950        assert_eq!(
1951            found[0].to_string(),
1952            "!0: a metadata node's parent comes before it and this one is !0"
1953        );
1954    }
1955
1956    #[test]
1957    fn a_datalayout_the_target_does_not_imply_is_reported() {
1958        let mut names = Interner::new();
1959        let mut module = Module::new(names.intern("built.c"), &target());
1960        module.datalayout = DataLayout::parse("e-p:32:32-i64:64-f80:32-S64").expect("a layout");
1961        let found = match verify(&module, &names) {
1962            Ok(()) => panic!("that was expected to be turned down"),
1963            Err(errors) => errors,
1964        };
1965        assert_eq!(found.len(), 1, "{found:#?}");
1966        assert!(found[0].to_string().starts_with("@built.c: the datalayout is "), "{}", found[0]);
1967    }
1968
1969    #[test]
1970    fn a_flag_riding_along_where_it_is_read_is_not_reported() {
1971        let mut names = Interner::new();
1972        let module = Module::new(names.intern("built.c"), &target());
1973        let i32_ = Type::int(32);
1974        let mut func = Func::new(
1975            names.intern("f"),
1976            Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
1977        );
1978        let block = func.create_block();
1979        let param = func.append_param(block, i32_);
1980        let mut b = Builder::new(&mut func, block);
1981        let sum = b.binary(Opcode::Add, param, param, Flags::NSW);
1982        b.ret(&[sum]);
1983        assert!(verify_func(&module, &func, &names).is_ok());
1984    }
1985
1986    #[test]
1987    fn a_declaration_is_checked_and_has_nothing_else_to_check() {
1988        let mut names = Interner::new();
1989        let module = Module::new(names.intern("built.c"), &target());
1990        let func = Func::new(Symbol::from_raw(0), Signature::new());
1991        assert!(func.is_declaration());
1992        assert!(verify_func(&module, &func, &names).is_ok());
1993    }
1994}