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::{Slot, TargetInfo};
46
47use crate::func::Func;
48use crate::inst::{Abi, Block, CallInfo, Def, Inst, Param, Signature, VaInfo, 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    /// What a call says about the arguments its signature does not name.
307    ///
308    /// The list is empty when they all travel as the values in hand, so what is checked is the
309    /// other case: there is one entry for each of them, the call is variadic because nothing
310    /// else has such an argument, and each entry describes something an argument can do. An
311    /// `sret` cannot be one of them, since the space for a return value arrives first and the
312    /// first argument is one the signature names.
313    fn varargs(
314        &mut self,
315        func: &'a Func,
316        info: &CallInfo,
317        passed: usize,
318        arg: impl Fn(usize) -> Type,
319    ) {
320        let varargs = &func[info.varargs];
321        if varargs.is_empty() {
322            return;
323        }
324        let named = func[info.signature].params.len();
325        if !func[info.signature].variadic {
326            self.error("this call is not variadic and says how a variadic argument travels");
327            return;
328        }
329        if varargs.len() != passed.saturating_sub(named) {
330            self.error(format!(
331                "the call passes {} arguments the signature does not name and says how {} travel",
332                passed.saturating_sub(named),
333                varargs.len()
334            ));
335            return;
336        }
337        for (index, &abi) in varargs.iter().enumerate() {
338            let at = format!("argument {}", named + index + 1);
339            if matches!(abi, Abi::Sret { .. }) {
340                self.error(format!("{at} is an sret and only a parameter can be one"));
341                continue;
342            }
343            self.abi(&at, &Param { ty: arg(index + named), abi });
344        }
345    }
346
347    /// One parameter's attribute against its type.
348    fn abi(&mut self, at: &str, param: &Param) {
349        match param.abi {
350            Abi::Plain => {}
351            Abi::Sext | Abi::Zext => {
352                if !param.ty.is_int() || param.ty.is_vector() {
353                    self.error(format!("{at} is extended and {} is not an integer", param.ty));
354                }
355            }
356            Abi::ByVal { size, align } | Abi::Sret { size, align } => {
357                if !param.ty.is_ptr() {
358                    self.error(format!(
359                        "{at} travels indirectly and {} is not a pointer",
360                        param.ty
361                    ));
362                }
363                if !align.is_power_of_two() {
364                    self.error(format!("an alignment is a power of two and this is {align}"));
365                }
366                if size == 0 {
367                    self.error(format!("{at} travels indirectly and has no size"));
368                }
369            }
370        }
371    }
372
373    /// Every value and every block an instruction names is one the function has.
374    ///
375    /// This is separate and comes first because everything else reads a `ValueData` or a
376    /// `BlockData` out of a table, and an index past the end of one is a panic rather than a
377    /// diagnosis.
378    fn bounds(&mut self, func: &'a Func) -> bool {
379        let counts = func.counts();
380        let before = self.errors.len();
381        for block in func.blocks() {
382            self.block = Some(block);
383            for &value in &func[block].params {
384                if value.index() >= counts.values {
385                    self.error(format!(
386                        "parameter %{} is not a value of this function",
387                        value.raw()
388                    ));
389                }
390            }
391            for inst in func.insts(block) {
392                self.inst = Some(inst);
393                for &value in &func[func[inst].args] {
394                    if value.index() >= counts.values {
395                        self.error(format!("%{} is not a value of this function", value.raw()));
396                    }
397                }
398                for value in func[inst].results() {
399                    if value.index() >= counts.values {
400                        self.error(format!("%{} is not a value of this function", value.raw()));
401                    }
402                }
403                for call in func.successors(inst) {
404                    if call.block.index() >= counts.blocks {
405                        self.error(format!(
406                            "block{} is not a block of this function",
407                            call.block.raw()
408                        ));
409                    }
410                    for &value in &func[call.args] {
411                        if value.index() >= counts.values {
412                            self.error(format!("%{} is not a value of this function", value.raw()));
413                        }
414                    }
415                }
416            }
417            self.inst = None;
418        }
419        self.block = None;
420        self.errors.len() == before
421    }
422
423    fn block(&mut self, func: &'a Func, block: Block, doms: &Doms, layout: &Layout) {
424        if !doms.reaches(block) {
425            self.error("this block is not reachable and has not been deleted");
426        }
427        if block == func.entry().expect("checked in func") {
428            for other in func.blocks() {
429                let last = func[other].last;
430                if last.is_some_and(|inst| func.successors(inst).any(|call| call.block == block)) {
431                    self.error("the entry block is branched to, and it takes the arguments");
432                }
433            }
434        }
435
436        let mut seen_terminator = false;
437        for inst in func.insts(block) {
438            self.inst = Some(inst);
439            if seen_terminator {
440                self.error("this comes after the block's terminator");
441            }
442            seen_terminator |= func.is_terminator(inst);
443            self.inst(func, inst, doms, layout);
444        }
445        self.inst = None;
446        if !seen_terminator {
447            self.error("this block does not end in a terminator");
448        }
449    }
450
451    fn inst(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
452        let data = &func[inst];
453        let opcode = data.opcode;
454
455        let stray = data.flags.without(crate::Flags::legal_on(opcode));
456        if !stray.is_empty() {
457            let names: Vec<&str> = stray.iter().map(|(_, name)| name).collect();
458            self.error(format!("{} does not read `{}`", opcode.name(), names.join("`, `")));
459        }
460        if data.extra.kind() != opcode.extra_kind() {
461            // An instruction carrying some other opcode's payload prints as text the parser
462            // cannot read, so this is caught here rather than found as a round trip failure.
463            self.error(format!(
464                "{} carries {} and this one carries {}",
465                opcode.name(),
466                opcode.extra_kind().name(),
467                data.extra.kind().name()
468            ));
469        }
470        if let Some(want) = opcode.results() {
471            if want != data.results {
472                self.error(format!(
473                    "{} produces {want} values and this one produces {}",
474                    opcode.name(),
475                    data.results
476                ));
477            }
478        }
479
480        self.uses(func, inst, doms, layout);
481        self.branches(func, inst);
482        self.memory(func, inst);
483        self.shape(func, inst);
484    }
485
486    /// Every use is reached by its definition.
487    fn uses(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
488        let block = layout.block_of(inst).expect("walking the blocks");
489        let check = |verifier: &mut Self, value: Value| match func[value].def {
490            Def::Param { block: def, .. } => {
491                if !doms.dominates(def, block) {
492                    verifier.error(format!(
493                        "%{} arrives at block{} and does not reach here",
494                        value.raw(),
495                        def.raw()
496                    ));
497                }
498            }
499            Def::Result { inst: def, .. } => {
500                let Some(def_block) = layout.block_of(def) else {
501                    verifier.error(format!(
502                        "%{} is produced by an instruction that is not in the function",
503                        value.raw()
504                    ));
505                    return;
506                };
507                let reaches = if def_block == block {
508                    layout.position(def) < layout.position(inst)
509                } else {
510                    doms.dominates(def_block, block)
511                };
512                if !reaches {
513                    verifier.error(format!(
514                        "%{} is produced in block{} and does not reach here",
515                        value.raw(),
516                        def_block.raw()
517                    ));
518                }
519            }
520        };
521        for &value in &func[func[inst].args] {
522            check(self, value);
523        }
524        for call in func.successors(inst) {
525            for &value in &func[call.args] {
526                check(self, value);
527            }
528        }
529    }
530
531    /// Every branch passes what the block it goes to takes.
532    fn branches(&mut self, func: &'a Func, inst: Inst) {
533        if func[inst].opcode == Opcode::BlockAddr {
534            // The one instruction that names a block without arriving at it, so the block's
535            // parameters are nothing to do with it. That it passes no arguments is checked
536            // with the rest of its shape.
537            return;
538        }
539        for call in func.successors(inst) {
540            let params = &func[call.block].params;
541            let args = &func[call.args];
542            if params.len() != args.len() {
543                self.error(format!(
544                    "block{} takes {} arguments and this branch passes {}",
545                    call.block.raw(),
546                    params.len(),
547                    args.len()
548                ));
549                continue;
550            }
551            for (index, (&param, &arg)) in params.iter().zip(args).enumerate() {
552                let (want, got) = (func[param].ty, func[arg].ty);
553                if want != got {
554                    self.error(format!(
555                        "argument {} to block{} is {want} and this one is {got}",
556                        index + 1,
557                        call.block.raw()
558                    ));
559                }
560            }
561        }
562        if let Extra::Switch(info) = func[inst].extra {
563            let switch = &func[info];
564            let targets = &func[switch.targets];
565            let cases = &func[switch.cases];
566            if targets.len() != cases.len() + 1 {
567                self.error(format!(
568                    "a switch has one target per case and a default, and this one has {} targets for {} cases",
569                    targets.len(),
570                    cases.len()
571                ));
572            }
573            for (index, case) in cases.iter().enumerate() {
574                if cases[..index].contains(case) {
575                    self.error("two cases of this switch have the same value");
576                }
577            }
578        }
579    }
580
581    /// What an access says about itself.
582    fn memory(&mut self, func: &'a Func, inst: Inst) {
583        let opcode = func[inst].opcode;
584        let info = match func[inst].extra {
585            Extra::Mem(at) => func[at],
586            Extra::Rmw(_, at) => func[at],
587            Extra::VaObject(at) => {
588                let object = func[at];
589                self.slots(func, object);
590                func[object.mem]
591            }
592            Extra::Order(order) => {
593                if !order.is_valid_for_rmw() {
594                    self.error("a fence is not a fence unless it orders something");
595                }
596                return;
597            }
598            _ => return,
599        };
600        if !info.align.is_power_of_two() {
601            self.error(format!("an alignment is a power of two and this is {}", info.align));
602        }
603        if let Some(tbaa) = info.tbaa {
604            if tbaa.index() >= self.module.counts().metadata {
605                self.error(format!("!{} is not a metadata node of this module", tbaa.raw()));
606            }
607        }
608        let ok = match opcode {
609            Opcode::AtomicLoad => info.order.is_valid_for_load(),
610            Opcode::AtomicStore => info.order.is_valid_for_store(),
611            Opcode::AtomicRmw | Opcode::Cmpxchg => info.order.is_valid_for_rmw(),
612            // Everything else is the non-atomic form, and the atomic form is a different
613            // opcode, so an ordering here is one somebody meant to put on that one.
614            _ => info.order == MemOrder::NotAtomic,
615        };
616        if !ok {
617            self.error(format!("{} cannot be asked for {}", opcode.name(), info.order));
618        }
619        if matches!(opcode, Opcode::Memcpy | Opcode::Memmove | Opcode::Memset) && info.size == 0 {
620            self.error(format!("{} moves no bytes", opcode.name()));
621        }
622    }
623
624    /// Where an object read off a variable argument list travelled.
625    ///
626    /// Each slot holds bytes of the object, so a slot that reaches past the end of it is a
627    /// classification that does not belong to this object and the backend would read memory the
628    /// object never had.
629    fn slots(&mut self, func: &'a Func, object: VaInfo) {
630        let size = func[object.mem].size;
631        for &slot in &func[object.slots] {
632            let width = match slot {
633                Slot::Integer { size, .. } => u64::from(size),
634                Slot::Float { format, .. } => u64::from(format.width()).div_ceil(8),
635            };
636            if slot.offset() + width > size {
637                self.error(format!(
638                    "a slot holds bytes {} to {} of an object of {size} bytes",
639                    slot.offset(),
640                    slot.offset() + width
641                ));
642            }
643        }
644    }
645
646    /// The operand and result types, by the rules of section 8.2.
647    ///
648    /// The shape of each group is written out rather than derived from a table, because the
649    /// groups do not have the same shape as each other and a table general enough to hold all
650    /// of them would be harder to read than this.
651    #[expect(clippy::too_many_lines, reason = "one arm per group of opcodes, and they differ")]
652    fn shape(&mut self, func: &'a Func, inst: Inst) {
653        let data = &func[inst];
654        let opcode = data.opcode;
655        let args = &func[data.args];
656        let arity = args.len();
657        let arg = |n: usize| func[args[n]].ty;
658        let results = usize::from(data.results);
659        let res = |n: usize| func[data.results().nth(n).expect("within the count")].ty;
660
661        match opcode {
662            // Constants take nothing and say their own type.
663            Opcode::IConst => {
664                if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_int() {
665                    self.error(format!(
666                        "iconst produces an integer and this one produces {}",
667                        res(0)
668                    ));
669                }
670            }
671            Opcode::FConst => {
672                if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_float() {
673                    self.error(format!(
674                        "fconst produces a floating point value and this one produces {}",
675                        res(0)
676                    ));
677                }
678            }
679            Opcode::Splat => {
680                if self.takes(opcode, arity, 0) && results == 1 && !res(0).is_vector() {
681                    self.error(format!("splat produces a vector and this one produces {}", res(0)));
682                }
683            }
684            Opcode::GlobalAddr
685            | Opcode::StackSave
686            | Opcode::FrameAddress
687            | Opcode::ReturnAddress => {
688                if results == 1 && !res(0).is_ptr() {
689                    self.error(format!(
690                        "{} produces a pointer and this one produces {}",
691                        opcode.name(),
692                        res(0)
693                    ));
694                }
695            }
696
697            // Integer arithmetic: two of one type, and that type back.
698            Opcode::Add
699            | Opcode::Sub
700            | Opcode::Mul
701            | Opcode::SDiv
702            | Opcode::UDiv
703            | Opcode::SRem
704            | Opcode::URem
705            | Opcode::And
706            | Opcode::Or
707            | Opcode::Xor
708            | Opcode::Shl
709            | Opcode::LShr
710            | Opcode::AShr => {
711                if self.takes(opcode, arity, 2) {
712                    self.integer(opcode, arg(0), 0);
713                    self.agree(opcode, arg(0), arg(1));
714                    if results == 1 {
715                        self.produces(opcode, res(0), arg(0));
716                    }
717                }
718            }
719
720            // Floating point arithmetic, the same shape with one more operand for `fma`.
721            Opcode::FAdd | Opcode::FSub | Opcode::FMul | Opcode::FDiv | Opcode::FRem => {
722                if self.takes(opcode, arity, 2) {
723                    self.floating(opcode, arg(0), 0);
724                    self.agree(opcode, arg(0), arg(1));
725                    if results == 1 {
726                        self.produces(opcode, res(0), arg(0));
727                    }
728                }
729            }
730            Opcode::FNeg => {
731                if self.takes(opcode, arity, 1) {
732                    self.floating(opcode, arg(0), 0);
733                    if results == 1 {
734                        self.produces(opcode, res(0), arg(0));
735                    }
736                }
737            }
738            Opcode::Fma => {
739                if self.takes(opcode, arity, 3) {
740                    self.floating(opcode, arg(0), 0);
741                    self.agree(opcode, arg(0), arg(1));
742                    self.agree(opcode, arg(0), arg(2));
743                    if results == 1 {
744                        self.produces(opcode, res(0), arg(0));
745                    }
746                }
747            }
748
749            // A comparison answers one bit per lane, whatever it compared.
750            Opcode::ICmp | Opcode::FCmp => {
751                if self.takes(opcode, arity, 2) {
752                    if opcode == Opcode::FCmp {
753                        self.floating(opcode, arg(0), 0);
754                    } else if !arg(0).lane().is_int() && !arg(0).is_ptr() {
755                        self.error(format!(
756                            "operand 1 of icmp is an integer or a pointer and this one is {}",
757                            arg(0)
758                        ));
759                    }
760                    self.agree(opcode, arg(0), arg(1));
761                    if results == 1 {
762                        self.produces(opcode, res(0), arg(0).with_lane(Type::I1));
763                    }
764                }
765            }
766
767            // Conversions, each of which says which way it goes and by how much.
768            Opcode::Trunc | Opcode::SExt | Opcode::ZExt => {
769                if self.takes(opcode, arity, 1) && results == 1 {
770                    self.integer(opcode, arg(0), 0);
771                    self.lanes(opcode, res(0), arg(0));
772                    self.widens(opcode, res(0), arg(0), opcode != Opcode::Trunc);
773                }
774            }
775            Opcode::FPTrunc | Opcode::FPExt => {
776                if self.takes(opcode, arity, 1) && results == 1 {
777                    self.floating(opcode, arg(0), 0);
778                    self.lanes(opcode, res(0), arg(0));
779                    self.widens(opcode, res(0), arg(0), opcode == Opcode::FPExt);
780                }
781            }
782            Opcode::FPToSI | Opcode::FPToUI => {
783                if self.takes(opcode, arity, 1) && results == 1 {
784                    self.floating(opcode, arg(0), 0);
785                    self.lanes(opcode, res(0), arg(0));
786                    if !res(0).lane().is_int() {
787                        self.error(format!(
788                            "{} produces an integer and this one produces {}",
789                            opcode.name(),
790                            res(0)
791                        ));
792                    }
793                }
794            }
795            Opcode::SIToFP | Opcode::UIToFP => {
796                if self.takes(opcode, arity, 1) && results == 1 {
797                    self.integer(opcode, arg(0), 0);
798                    self.lanes(opcode, res(0), arg(0));
799                    if !res(0).lane().is_float() {
800                        self.error(format!(
801                            "{} produces a floating point value and this one produces {}",
802                            opcode.name(),
803                            res(0)
804                        ));
805                    }
806                }
807            }
808            Opcode::PtrToInt => {
809                if self.takes(opcode, arity, 1) && results == 1 {
810                    self.pointer(opcode, arg(0), 0);
811                    self.integer(opcode, res(0), 0);
812                }
813            }
814            Opcode::IntToPtr => {
815                if self.takes(opcode, arity, 1) && results == 1 {
816                    self.integer(opcode, arg(0), 0);
817                    if !res(0).is_ptr() {
818                        self.error(format!(
819                            "inttoptr produces a pointer and this one produces {}",
820                            res(0)
821                        ));
822                    }
823                }
824            }
825            Opcode::Bitcast => {
826                if self.takes(opcode, arity, 1) && results == 1 {
827                    let (from, to) = (arg(0), res(0));
828                    if from.is_ptr() != to.is_ptr() {
829                        // Between an address and a number there is a conversion of its own,
830                        // and using this one would hide it from anything looking for one.
831                        self.error(
832                            "a bitcast between a pointer and a number is ptrtoint or inttoptr",
833                        );
834                    } else if width(from) != width(to) {
835                        self.error(format!("a bitcast keeps the width and {from} and {to} differ"));
836                    }
837                }
838            }
839
840            // Memory.
841            Opcode::Alloca => {
842                if arity > 1 {
843                    self.takes(opcode, arity, 1);
844                } else if arity == 1 {
845                    self.integer(opcode, arg(0), 0);
846                } else if func.block_of(inst) != func.entry() {
847                    // One of a fixed size in a loop is a stack that grows every time round,
848                    // which is what the dynamic form asks for explicitly.
849                    self.error("an alloca of a fixed size belongs in the entry block");
850                }
851                if results == 1 && !res(0).is_ptr() {
852                    self.error(format!(
853                        "alloca produces a pointer and this one produces {}",
854                        res(0)
855                    ));
856                }
857            }
858            Opcode::Load | Opcode::AtomicLoad => {
859                if self.takes(opcode, arity, 1) {
860                    self.pointer(opcode, arg(0), 0);
861                }
862                if results == 1 && res(0).is_void() {
863                    self.error(format!("{} reads a value and void is not one", opcode.name()));
864                }
865            }
866            Opcode::Store | Opcode::AtomicStore => {
867                if self.takes(opcode, arity, 2) {
868                    if arg(0).is_void() {
869                        self.error(format!("{} writes a value and void is not one", opcode.name()));
870                    }
871                    self.pointer(opcode, arg(1), 1);
872                }
873            }
874            Opcode::PtrAdd => {
875                if self.takes(opcode, arity, 2) {
876                    self.pointer(opcode, arg(0), 0);
877                    self.integer(opcode, arg(1), 1);
878                    if results == 1 && !res(0).is_ptr() {
879                        self.error(format!(
880                            "ptr_add produces a pointer and this one produces {}",
881                            res(0)
882                        ));
883                    }
884                }
885            }
886            Opcode::Memcpy | Opcode::Memmove => {
887                if self.takes(opcode, arity, 2) {
888                    self.pointer(opcode, arg(0), 0);
889                    self.pointer(opcode, arg(1), 1);
890                }
891            }
892            Opcode::Memset => {
893                if self.takes(opcode, arity, 2) {
894                    self.pointer(opcode, arg(0), 0);
895                    self.integer(opcode, arg(1), 1);
896                }
897            }
898            Opcode::AtomicRmw => {
899                if self.takes(opcode, arity, 2) {
900                    self.pointer(opcode, arg(0), 0);
901                    self.integer(opcode, arg(1), 1);
902                    if results == 1 {
903                        self.produces(opcode, res(0), arg(1));
904                    }
905                }
906            }
907            Opcode::Cmpxchg => {
908                if self.takes(opcode, arity, 3) {
909                    self.pointer(opcode, arg(0), 0);
910                    self.agree(opcode, arg(1), arg(2));
911                    if results == 2 {
912                        self.produces(opcode, res(0), arg(1));
913                        self.produces(opcode, res(1), arg(1).with_lane(Type::I1));
914                    }
915                }
916            }
917            Opcode::Fence | Opcode::Unreachable | Opcode::UnreachableHint => {
918                self.takes(opcode, arity, 0);
919            }
920            Opcode::Prefetch | Opcode::StackRestore | Opcode::VaStart | Opcode::VaEnd => {
921                if self.takes(opcode, arity, 1) {
922                    self.pointer(opcode, arg(0), 0);
923                }
924            }
925            Opcode::VaCopy => {
926                if self.takes(opcode, arity, 2) {
927                    self.pointer(opcode, arg(0), 0);
928                    self.pointer(opcode, arg(1), 1);
929                }
930            }
931            Opcode::VaArg => {
932                if self.takes(opcode, arity, 1) {
933                    self.pointer(opcode, arg(0), 0);
934                }
935                if results == 1 && res(0).is_void() {
936                    self.error("va_arg reads a value and void is not one");
937                }
938            }
939            Opcode::VaObject => {
940                if self.takes(opcode, arity, 1) {
941                    self.pointer(opcode, arg(0), 0);
942                }
943                if results == 1 && res(0) != Type::PTR {
944                    self.error(format!(
945                        "va_object answers where the object is and {} is not an address",
946                        res(0)
947                    ));
948                }
949            }
950
951            // Control.
952            Opcode::Jump => {
953                self.takes(opcode, arity, 0);
954                self.targets(func, inst, 1);
955            }
956            Opcode::BrIf => {
957                if self.takes(opcode, arity, 1) && arg(0) != Type::I1 {
958                    self.error(format!("br_if branches on an i1 and this one on {}", arg(0)));
959                }
960                self.targets(func, inst, 2);
961            }
962            Opcode::Switch => {
963                if self.takes(opcode, arity, 1) {
964                    self.integer(opcode, arg(0), 0);
965                }
966            }
967            Opcode::BlockAddr => {
968                self.takes(opcode, arity, 0);
969                self.targets(func, inst, 1);
970                if results == 1 && !res(0).is_ptr() {
971                    self.error(format!(
972                        "block_addr produces a pointer and this one produces {}",
973                        res(0)
974                    ));
975                }
976                if func.successors(inst).any(|call| !func[call.args].is_empty()) {
977                    // Taking the address is not arriving, so there is nothing to hand over.
978                    // What the block takes is passed by the branch that goes there.
979                    self.error("block_addr names a block and passes it arguments");
980                }
981            }
982            Opcode::IndirectBr => {
983                if self.takes(opcode, arity, 1) {
984                    self.pointer(opcode, arg(0), 0);
985                }
986                // No count to check: how many blocks one of these can arrive at is how many
987                // the front end says, and a `goto *p` in a function with one label has one.
988            }
989            Opcode::Return => {
990                let want = &func.signature().returns;
991                if arity != want.len() {
992                    self.error(format!(
993                        "the signature returns {} and this returns {arity}",
994                        want.len()
995                    ));
996                } else {
997                    for (index, ty) in want.iter().map(|param| param.ty).enumerate() {
998                        if arg(index) != ty {
999                            self.error(format!(
1000                                "result {} of the signature is {ty} and this returns {}",
1001                                index + 1,
1002                                arg(index)
1003                            ));
1004                        }
1005                    }
1006                }
1007            }
1008
1009            // Calls, whose signature is what says the shape.
1010            Opcode::Call | Opcode::CallIndirect | Opcode::TailCall => {
1011                let Extra::Call(at) = data.extra else { return };
1012                let info = func[at];
1013                let signature = &func[info.signature];
1014                let indirect = usize::from(opcode == Opcode::CallIndirect);
1015                if indirect == 1 {
1016                    if arity == 0 {
1017                        self.error("call_indirect calls through a pointer and has no operands");
1018                        return;
1019                    }
1020                    self.pointer(opcode, arg(0), 0);
1021                }
1022                let passed = arity - indirect;
1023                let enough = if signature.variadic {
1024                    passed >= signature.params.len()
1025                } else {
1026                    passed == signature.params.len()
1027                };
1028                if enough {
1029                    for (index, ty) in signature.param_types().enumerate() {
1030                        if arg(index + indirect) != ty {
1031                            self.error(format!(
1032                                "parameter {} of the signature is {ty} and this argument is {}",
1033                                index + 1,
1034                                arg(index + indirect)
1035                            ));
1036                        }
1037                    }
1038                } else {
1039                    self.error(format!(
1040                        "the signature takes {}{} and this call passes {passed}",
1041                        signature.params.len(),
1042                        if signature.variadic { " or more" } else { "" }
1043                    ));
1044                }
1045                self.varargs(func, &info, passed, |n| arg(n + indirect));
1046                if results != signature.returns.len() {
1047                    self.error(format!(
1048                        "the signature returns {} and this call produces {results}",
1049                        signature.returns.len()
1050                    ));
1051                } else {
1052                    for (index, ty) in signature.return_types().enumerate() {
1053                        if res(index) != ty {
1054                            self.error(format!(
1055                                "result {} of the signature is {ty} and this call produces {}",
1056                                index + 1,
1057                                res(index)
1058                            ));
1059                        }
1060                    }
1061                }
1062                // Where the callee is in this module, the signature at the call and the one at
1063                // the function are the same signature or one of them is wrong.
1064                if let Some(callee) = info.callee {
1065                    if let Some(SymbolRef::Func(id)) = self.module.lookup(callee) {
1066                        if self.module[id].signature() != signature {
1067                            self.error(format!(
1068                                "@{} is declared here with another signature",
1069                                self.names.resolve(callee)
1070                            ));
1071                        }
1072                    }
1073                }
1074            }
1075
1076            // Bit counting, which answers in the type it was asked about.
1077            Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop | Opcode::Bswap | Opcode::Bitreverse => {
1078                if self.takes(opcode, arity, 1) {
1079                    self.integer(opcode, arg(0), 0);
1080                    if results == 1 {
1081                        self.produces(opcode, res(0), arg(0));
1082                    }
1083                }
1084            }
1085
1086            // The overflow-checked forms, which answer the value and whether it wrapped.
1087            Opcode::SAddOverflow
1088            | Opcode::UAddOverflow
1089            | Opcode::SSubOverflow
1090            | Opcode::USubOverflow
1091            | Opcode::SMulOverflow
1092            | Opcode::UMulOverflow => {
1093                if self.takes(opcode, arity, 2) {
1094                    self.integer(opcode, arg(0), 0);
1095                    self.agree(opcode, arg(0), arg(1));
1096                    if results == 2 {
1097                        self.produces(opcode, res(0), arg(0));
1098                        self.produces(opcode, res(1), arg(0).with_lane(Type::I1));
1099                    }
1100                }
1101            }
1102            Opcode::Expect => {
1103                if self.takes(opcode, arity, 2) {
1104                    self.agree(opcode, arg(0), arg(1));
1105                    if results == 1 {
1106                        self.produces(opcode, res(0), arg(0));
1107                    }
1108                }
1109            }
1110
1111            // What is left says nothing about its operands here: the two markers are placed by
1112            // the front end around code the optimizer must not move, inline assembly is
1113            // whatever its constraints say, and a target intrinsic is the target's own rule.
1114            Opcode::SetjmpMarker
1115            | Opcode::LongjmpMarker
1116            | Opcode::InlineAsm
1117            | Opcode::TargetIntrinsic => {}
1118        }
1119    }
1120
1121    // The small questions the shapes are made of.
1122
1123    /// Reports the operand count when it is wrong, and answers whether it was right.
1124    fn takes(&mut self, opcode: Opcode, got: usize, want: usize) -> bool {
1125        if got == want {
1126            return true;
1127        }
1128        self.error(format!("{} takes {want} operands and this one has {got}", opcode.name()));
1129        false
1130    }
1131
1132    /// Reports the number of branch targets when it is wrong.
1133    fn targets(&mut self, func: &'a Func, inst: Inst, want: usize) {
1134        let got = func.successors(inst).count();
1135        if got != want {
1136            self.error(format!(
1137                "{} branches to {want} blocks and this one to {got}",
1138                func[inst].opcode.name()
1139            ));
1140        }
1141    }
1142
1143    fn integer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1144        if !ty.lane().is_int() {
1145            self.error(format!(
1146                "operand {} of {} is an integer and this one is {ty}",
1147                n + 1,
1148                opcode.name()
1149            ));
1150        }
1151    }
1152
1153    fn floating(&mut self, opcode: Opcode, ty: Type, n: usize) {
1154        if !ty.lane().is_float() {
1155            self.error(format!(
1156                "operand {} of {} is a floating point value and this one is {ty}",
1157                n + 1,
1158                opcode.name()
1159            ));
1160        }
1161    }
1162
1163    fn pointer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1164        if !ty.is_ptr() {
1165            self.error(format!(
1166                "operand {} of {} is a pointer and this one is {ty}",
1167                n + 1,
1168                opcode.name()
1169            ));
1170        }
1171    }
1172
1173    /// Two operands that have to be the same type as each other.
1174    fn agree(&mut self, opcode: Opcode, first: Type, second: Type) {
1175        if first != second {
1176            self.error(format!(
1177                "the operands of {} have one type and these are {first} and {second}",
1178                opcode.name()
1179            ));
1180        }
1181    }
1182
1183    /// A result that has to be a particular type.
1184    fn produces(&mut self, opcode: Opcode, got: Type, want: Type) {
1185        if got != want {
1186            self.error(format!(
1187                "{} produces {want} here and this one produces {got}",
1188                opcode.name()
1189            ));
1190        }
1191    }
1192
1193    /// A conversion that keeps the lane count, since none of them changes it.
1194    fn lanes(&mut self, opcode: Opcode, to: Type, from: Type) {
1195        if to.lanes() != from.lanes() {
1196            self.error(format!(
1197                "{} keeps the lane count and {from} has {} and {to} has {}",
1198                opcode.name(),
1199                from.lanes(),
1200                to.lanes()
1201            ));
1202        }
1203    }
1204
1205    /// A conversion that has to go the way its name says.
1206    fn widens(&mut self, opcode: Opcode, to: Type, from: Type, wider: bool) {
1207        let (a, b) = (to.lane().bits(), from.lane().bits());
1208        let ok = if wider { a > b } else { a < b };
1209        if !ok {
1210            let way = if wider { "wider" } else { "narrower" };
1211            self.error(format!(
1212                "{} produces something {way} and {from} to {to} is not",
1213                opcode.name()
1214            ));
1215        }
1216    }
1217
1218    // Reporting.
1219
1220    fn error(&mut self, message: impl Into<String>) {
1221        let at = self.locate();
1222        self.at(at, message);
1223    }
1224
1225    fn at(&mut self, at: String, message: impl Into<String>) {
1226        self.errors.push(VerifyError { at, message: message.into() });
1227    }
1228
1229    /// Where the walk is, as text.
1230    ///
1231    /// A block is named by its index rather than by the number the printer would give it,
1232    /// which is the same number for a module that came from the parser and can differ for one
1233    /// a pass has been reordering.
1234    fn locate(&self) -> String {
1235        use fmt::Write as _;
1236        let mut at = String::new();
1237        if let Some(func) = self.func {
1238            let _ = write!(at, "@{}", self.names.resolve(func.name));
1239            if let Some(block) = self.block {
1240                let _ = write!(at, " block{}", block.raw());
1241            }
1242            if let Some(inst) = self.inst {
1243                let _ = write!(at, " {}", func[inst].opcode.name());
1244            }
1245        }
1246        at
1247    }
1248}
1249
1250/// Where each instruction is, so that a use in the same block as its definition can be told
1251/// from a use before it.
1252struct Layout {
1253    block: Vec<Option<Block>>,
1254    position: Vec<u32>,
1255}
1256
1257impl Layout {
1258    fn new(func: &Func) -> Self {
1259        let counts = func.counts();
1260        let mut layout =
1261            Layout { block: vec![None; counts.insts], position: vec![0; counts.insts] };
1262        for block in func.blocks() {
1263            for (position, inst) in func.insts(block).enumerate() {
1264                layout.block[inst.index()] = Some(block);
1265                layout.position[inst.index()] = position as u32;
1266            }
1267        }
1268        layout
1269    }
1270
1271    fn block_of(&self, inst: Inst) -> Option<Block> {
1272        self.block[inst.index()]
1273    }
1274
1275    fn position(&self, inst: Inst) -> u32 {
1276        self.position[inst.index()]
1277    }
1278}
1279
1280/// Which block dominates which, by the iterative algorithm of Cooper, Harvey and Kennedy,
1281/// "A Simple, Fast Dominance Algorithm" (2001).
1282///
1283/// The one in the verifier rather than a shared analysis, because the optimizer's dominator
1284/// tree is incrementally maintained across a pass and this one is built from nothing every time
1285/// it is asked for. The two want different things from the same idea.
1286struct Doms {
1287    /// Where each block is in reverse postorder, which is the order the fixed point converges
1288    /// fastest in, and `None` for one the entry does not reach.
1289    rank: Vec<Option<u32>>,
1290    /// The rank of each block's immediate dominator, indexed by rank.
1291    idom: Vec<u32>,
1292}
1293
1294impl Doms {
1295    fn new(func: &Func) -> Self {
1296        let counts = func.counts();
1297        let entry = func.entry().expect("a function with blocks has a first one");
1298
1299        // Every instruction that names a block, and not only the terminator. The one other
1300        // instruction that names one is `block_addr`, whose block is somewhere an
1301        // `indirect_br` can arrive at from anywhere the address reaches, so counting it as an
1302        // edge is what keeps a label that is only jumped to indirectly out of the reachability
1303        // report. The edge is a real one in the only direction that matters here: it can add
1304        // predecessors to a block and so take dominators away from it, which makes the check
1305        // on the uses stricter and never looser.
1306        let mut succs: Vec<Vec<Block>> = vec![Vec::new(); counts.blocks];
1307        for block in func.blocks() {
1308            for inst in func.insts(block) {
1309                succs[block.index()].extend(func.successors(inst).map(|call| call.block));
1310            }
1311        }
1312
1313        // Postorder by an explicit stack, since a chain of blocks can be as long as the
1314        // function is and the recursive form would run out of stack on one.
1315        let mut order = Vec::new();
1316        let mut seen = vec![false; counts.blocks];
1317        let mut stack = vec![(entry, 0usize)];
1318        seen[entry.index()] = true;
1319        while let Some((block, next)) = stack.pop() {
1320            match succs[block.index()].get(next) {
1321                Some(&target) => {
1322                    stack.push((block, next + 1));
1323                    if !seen[target.index()] {
1324                        seen[target.index()] = true;
1325                        stack.push((target, 0));
1326                    }
1327                }
1328                None => order.push(block),
1329            }
1330        }
1331        order.reverse();
1332
1333        let mut rank = vec![None; counts.blocks];
1334        for (index, &block) in order.iter().enumerate() {
1335            rank[block.index()] = Some(index as u32);
1336        }
1337        let mut preds: Vec<Vec<u32>> = vec![Vec::new(); order.len()];
1338        for (index, &block) in order.iter().enumerate() {
1339            for &target in &succs[block.index()] {
1340                if let Some(target) = rank[target.index()] {
1341                    preds[target as usize].push(index as u32);
1342                }
1343            }
1344        }
1345
1346        // The entry dominates itself, and everything else starts undefined, which is what the
1347        // sentinel is. The fixed point is reached in one pass over a reducible CFG and in two
1348        // over the ones a `goto` produces.
1349        const NONE: u32 = u32::MAX;
1350        let mut idom = vec![NONE; order.len()];
1351        if !order.is_empty() {
1352            idom[0] = 0;
1353        }
1354        let mut changed = true;
1355        while changed {
1356            changed = false;
1357            for index in 1..order.len() {
1358                let mut new = NONE;
1359                for &pred in &preds[index] {
1360                    if idom[pred as usize] == NONE {
1361                        continue;
1362                    }
1363                    new = if new == NONE { pred } else { meet(&idom, new, pred) };
1364                }
1365                if new != NONE && idom[index] != new {
1366                    idom[index] = new;
1367                    changed = true;
1368                }
1369            }
1370        }
1371        Doms { rank, idom }
1372    }
1373
1374    /// Whether the entry block reaches this one at all.
1375    fn reaches(&self, block: Block) -> bool {
1376        self.rank[block.index()].is_some()
1377    }
1378
1379    /// Whether every path from the entry to `block` goes through `of`.
1380    ///
1381    /// A block the entry does not reach is dominated by everything, which is vacuously true and
1382    /// keeps an unreachable block from being reported twice over, once for being unreachable
1383    /// and once for every value it uses.
1384    fn dominates(&self, of: Block, block: Block) -> bool {
1385        let (Some(a), Some(b)) = (self.rank[of.index()], self.rank[block.index()]) else {
1386            return true;
1387        };
1388        let mut walk = b;
1389        while walk > a {
1390            walk = self.idom[walk as usize];
1391        }
1392        walk == a
1393    }
1394}
1395
1396/// The nearest block that dominates both, walking the two chains towards the entry.
1397fn meet(idom: &[u32], mut a: u32, mut b: u32) -> u32 {
1398    while a != b {
1399        while a > b {
1400            a = idom[a as usize];
1401        }
1402        while b > a {
1403            b = idom[b as usize];
1404        }
1405    }
1406    a
1407}
1408
1409/// How many bits a value of that type occupies, counting every lane.
1410fn width(ty: Type) -> u64 {
1411    u64::from(ty.bits()) * u64::from(ty.lanes())
1412}
1413
1414/// A list of types, as the text writes them.
1415fn types(list: &[Type]) -> String {
1416    if list.is_empty() {
1417        return "nothing".to_string();
1418    }
1419    list.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
1420}
1421
1422#[cfg(test)]
1423mod tests {
1424    use rucc_base::{Idx, Interner, Symbol};
1425    use rucc_diag::Span;
1426    use rucc_target::{Arch, Env, Os, Triple};
1427
1428    use super::*;
1429    use crate::fixtures::{EXAMPLE, SYMBOLS, ZOO};
1430    use crate::func::Builder;
1431    use crate::inst::{InstData, MetaNode, Signature};
1432    use crate::{Flags, IntPred, parse};
1433
1434    fn target() -> TargetInfo {
1435        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1436    }
1437
1438    /// Everything wrong with a module written as text, which is how most of these are written:
1439    /// the parser turns down what is malformed and what is left is what the verifier is for.
1440    fn errors(text: &str) -> Vec<String> {
1441        let mut names = Interner::new();
1442        let module = match parse(text, &mut names) {
1443            Ok(module) => module,
1444            Err(error) => panic!("{error}"),
1445        };
1446        match verify(&module, &names) {
1447            Ok(()) => Vec::new(),
1448            Err(errors) => errors.iter().map(ToString::to_string).collect(),
1449        }
1450    }
1451
1452    /// The one thing wrong with it.
1453    fn only(text: &str) -> String {
1454        let found = errors(text);
1455        assert_eq!(found.len(), 1, "{found:#?}");
1456        found.into_iter().next().expect("just counted one")
1457    }
1458
1459    const HEADER: &str = "\
1460; ModuleID = 'bad.c'
1461; format 0
1462target triple = \"x86_64-unknown-linux-gnu\"
1463target datalayout = \"e-p:64:64-i64:64-f80:128-S128\"
1464";
1465
1466    /// A function around that body, with that signature.
1467    fn wrap(signature: &str, body: &str) -> String {
1468        format!("{HEADER}\nfunc @f{signature}, linkage(external) {{\n{body}}}\n")
1469    }
1470
1471    #[test]
1472    fn the_three_fixtures_are_modules_the_compiler_may_believe() {
1473        for text in [EXAMPLE, ZOO, SYMBOLS] {
1474            assert_eq!(errors(text), Vec::<String>::new());
1475        }
1476    }
1477
1478    #[test]
1479    fn a_use_that_its_definition_does_not_reach_is_reported() {
1480        let text = wrap(
1481            "(i1) -> i32",
1482            "block0(%0: i1):
1483    br_if %0, block1, block2
1484
1485block1:
1486    %1 = iconst.i32 7
1487    jump block2
1488
1489block2:
1490    return %1
1491",
1492        );
1493        assert_eq!(
1494            only(&text),
1495            "@f block2 return: %1 is produced in block1 and does not reach here"
1496        );
1497    }
1498
1499    #[test]
1500    fn a_use_before_its_definition_in_the_same_block_is_reported() {
1501        let text = wrap(
1502            "() -> i32",
1503            "block0:
1504    %0 = iconst.i32 1
1505    %1 = add %2, %0
1506    %2 = iconst.i32 2
1507    return %1
1508",
1509        );
1510        assert_eq!(only(&text), "@f block0 add: %2 is produced in block0 and does not reach here");
1511    }
1512
1513    #[test]
1514    fn a_call_that_takes_its_arguments_the_way_the_abi_says_is_believed() {
1515        let text = wrap(
1516            "(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext)",
1517            "block0(%0: ptr, %1: ptr, %2: i8):
1518    return
1519",
1520        );
1521        assert_eq!(errors(&text), Vec::<String>::new());
1522    }
1523
1524    #[test]
1525    fn a_call_that_says_how_an_argument_past_its_parameter_list_travels_is_believed() {
1526        let text = wrap(
1527            "(ptr)",
1528            "block0(%0: ptr):
1529    call @p(%0, %0 byval(24, align 8)) : (ptr, ...)
1530    return
1531",
1532        );
1533        assert_eq!(errors(&text), Vec::<String>::new());
1534    }
1535
1536    #[test]
1537    fn a_call_that_says_it_of_something_that_is_not_a_pointer_is_reported() {
1538        let text = wrap(
1539            "(ptr)",
1540            "block0(%0: ptr):
1541    %1 = iconst.i32 1
1542    call @p(%0, %1 byval(4, align 4)) : (ptr, ...)
1543    return
1544",
1545        );
1546        assert_eq!(
1547            only(&text),
1548            "@f block0 call: argument 2 travels indirectly and i32 is not a pointer"
1549        );
1550    }
1551
1552    #[test]
1553    fn a_call_that_says_an_argument_past_its_parameter_list_is_an_sret_is_reported() {
1554        let text = wrap(
1555            "(ptr)",
1556            "block0(%0: ptr):
1557    call @p(%0, %0 sret(24, align 8)) : (ptr, ...)
1558    return
1559",
1560        );
1561        assert_eq!(
1562            only(&text),
1563            "@f block0 call: argument 2 is an sret and only a parameter can be one"
1564        );
1565    }
1566
1567    #[test]
1568    fn a_call_that_is_not_variadic_says_nothing_about_a_variadic_argument() {
1569        let text = wrap(
1570            "(ptr)",
1571            "block0(%0: ptr):
1572    call @p(%0, %0 byval(24, align 8)) : (ptr)
1573    return
1574",
1575        );
1576        assert_eq!(
1577            errors(&text),
1578            vec![
1579                "@f block0 call: the signature takes 1 and this call passes 2",
1580                "@f block0 call: this call is not variadic and says how a variadic argument \
1581                 travels",
1582            ]
1583        );
1584    }
1585
1586    #[test]
1587    fn an_sret_that_is_not_the_first_parameter_is_reported() {
1588        let text = wrap(
1589            "(ptr byval(8, align 8), ptr sret(16, align 8))",
1590            "block0(%0: ptr, %1: ptr):
1591    return
1592",
1593        );
1594        assert_eq!(only(&text), "@f: sret is the first parameter and this one is not");
1595    }
1596
1597    #[test]
1598    fn a_function_returning_through_sret_returns_nothing_else() {
1599        let text = wrap(
1600            "(ptr sret(8, align 8)) -> i32",
1601            "block0(%0: ptr):
1602    %1 = iconst.i32 7
1603    return %1
1604",
1605        );
1606        assert_eq!(only(&text), "@f: a signature returning through sret returns nothing else");
1607    }
1608
1609    #[test]
1610    fn an_object_that_travels_indirectly_travels_behind_a_pointer() {
1611        let text = wrap(
1612            "(i32 byval(4, align 4))",
1613            "block0(%0: i32):
1614    return
1615",
1616        );
1617        assert_eq!(only(&text), "@f: parameter 1 travels indirectly and i32 is not a pointer");
1618    }
1619
1620    #[test]
1621    fn an_alignment_a_parameter_could_not_have_is_reported() {
1622        let text = wrap(
1623            "(ptr byval(24, align 3))",
1624            "block0(%0: ptr):
1625    return
1626",
1627        );
1628        assert_eq!(only(&text), "@f: an alignment is a power of two and this is 3");
1629    }
1630
1631    /// The slots say which bytes of the object came out of which register, so one reaching past
1632    /// the end of the object is a classification that belongs to some other object, and a backend
1633    /// acting on it would copy memory this object never had.
1634    #[test]
1635    fn a_slot_holding_bytes_the_object_does_not_have_is_reported() {
1636        let text = wrap(
1637            "(ptr) -> ptr",
1638            "block0(%0: ptr):
1639    %1 = va_object %0, size 12, align 8, in(int 8 at 0, int 8 at 8)
1640    return %1
1641",
1642        );
1643        assert_eq!(
1644            only(&text),
1645            "@f block0 va_object: a slot holds bytes 8 to 16 of an object of 12 bytes"
1646        );
1647    }
1648
1649    #[test]
1650    fn a_result_does_not_travel_indirectly() {
1651        // A return value too large for the registers comes back through a parameter, so this
1652        // says an ABI nothing implements.
1653        let text = wrap(
1654            "() -> ptr byval(16, align 8)",
1655            "block0:
1656    %0 = iconst.i64 0
1657    %1 = inttoptr.ptr %0
1658    return %1
1659",
1660        );
1661        assert_eq!(only(&text), "@f: result 1 travels indirectly and a result cannot");
1662    }
1663
1664    #[test]
1665    fn an_extension_is_asked_of_an_integer_and_not_of_anything_else() {
1666        let text = wrap(
1667            "(ptr zext)",
1668            "block0(%0: ptr):
1669    return
1670",
1671        );
1672        assert_eq!(only(&text), "@f: parameter 1 is extended and ptr is not an integer");
1673    }
1674
1675    #[test]
1676    fn a_branch_that_passes_the_wrong_number_of_arguments_is_reported() {
1677        let text = wrap(
1678            "(i32)",
1679            "block0(%0: i32):
1680    jump block1(%0)
1681
1682block1:
1683    return
1684",
1685        );
1686        assert_eq!(
1687            only(&text),
1688            "@f block0 jump: block1 takes 0 arguments and this branch passes 1"
1689        );
1690    }
1691
1692    #[test]
1693    fn a_branch_that_passes_the_wrong_type_is_reported() {
1694        let text = wrap(
1695            "(i32) -> i32",
1696            "block0(%0: i32):
1697    %1 = sext.i64 %0
1698    jump block1(%1)
1699
1700block1(%2: i32):
1701    return %2
1702",
1703        );
1704        assert_eq!(only(&text), "@f block0 jump: argument 1 to block1 is i32 and this one is i64");
1705    }
1706
1707    #[test]
1708    fn a_block_that_does_not_end_in_a_terminator_is_reported() {
1709        let text = wrap("()", "block0:\n    %0 = iconst.i32 1\n");
1710        assert_eq!(only(&text), "@f block0: this block does not end in a terminator");
1711    }
1712
1713    #[test]
1714    fn an_instruction_after_the_terminator_is_reported() {
1715        let text = wrap("()", "block0:\n    return\n    %0 = iconst.i32 1\n");
1716        assert_eq!(only(&text), "@f block0 iconst: this comes after the block's terminator");
1717    }
1718
1719    #[test]
1720    fn an_unreachable_block_is_reported() {
1721        let text = wrap("()", "block0:\n    return\n\nblock1:\n    return\n");
1722        assert_eq!(only(&text), "@f block1: this block is not reachable and has not been deleted");
1723    }
1724
1725    #[test]
1726    fn a_block_a_jump_to_an_address_arrives_at_is_an_ordinary_target() {
1727        let text = wrap(
1728            "(ptr) -> i32",
1729            "block0(%0: ptr):
1730    %1 = block_addr block1
1731    indirect_br %0, block1
1732
1733block1:
1734    %2 = iconst.i32 1
1735    return %2
1736",
1737        );
1738        assert_eq!(errors(&text), Vec::<String>::new());
1739    }
1740
1741    #[test]
1742    fn a_block_whose_address_is_taken_is_reached_by_the_taking_of_it() {
1743        // Nothing branches to block1 here and its address has left the function, so the one
1744        // thing that says it is still somewhere control can arrive at is the `block_addr`.
1745        let text = wrap(
1746            "() -> ptr",
1747            "block0:
1748    %0 = block_addr block1
1749    return %0
1750
1751block1:
1752    unreachable
1753",
1754        );
1755        assert_eq!(errors(&text), Vec::<String>::new());
1756    }
1757
1758    #[test]
1759    fn taking_the_address_of_a_block_and_passing_it_arguments_is_reported() {
1760        // Taking the address is not arriving, so there is nothing to hand over. What block1
1761        // takes is passed by whatever branches there.
1762        let text = wrap(
1763            "(i32) -> ptr",
1764            "block0(%0: i32):
1765    %1 = block_addr block1(%0)
1766    return %1
1767
1768block1(%2: i32):
1769    unreachable
1770",
1771        );
1772        assert_eq!(
1773            only(&text),
1774            "@f block0 block_addr: block_addr names a block and passes it arguments"
1775        );
1776    }
1777
1778    #[test]
1779    fn a_jump_to_something_that_is_not_an_address_is_reported() {
1780        let text = wrap(
1781            "(i32)",
1782            "block0(%0: i32):
1783    indirect_br %0, block1
1784
1785block1:
1786    return
1787",
1788        );
1789        assert_eq!(
1790            only(&text),
1791            "@f block0 indirect_br: operand 1 of indirect_br is a pointer and this one is i32"
1792        );
1793    }
1794
1795    #[test]
1796    fn a_branch_back_to_the_entry_block_is_reported() {
1797        // The entry block's parameters are the function's arguments, so a branch to it would be
1798        // a second place they arrive from.
1799        let text = wrap(
1800            "(i32)",
1801            "block0(%0: i32):
1802    jump block1
1803
1804block1:
1805    jump block0(%0)
1806",
1807        );
1808        assert_eq!(
1809            only(&text),
1810            "@f block0: the entry block is branched to, and it takes the arguments"
1811        );
1812    }
1813
1814    #[test]
1815    fn an_entry_block_that_does_not_take_the_arguments_is_reported() {
1816        let text = wrap("(i32)", "block0(%0: i64):\n    return\n");
1817        assert_eq!(only(&text), "@f: the entry block takes i64 and the signature says i32");
1818    }
1819
1820    #[test]
1821    fn a_flag_the_opcode_does_not_read_is_reported() {
1822        let text =
1823            wrap("(i32) -> i32", "block0(%0: i32):\n    %1 = add.exact %0, %0\n    return %1\n");
1824        assert_eq!(only(&text), "@f block0 add: add does not read `exact`");
1825    }
1826
1827    #[test]
1828    fn an_ordering_the_operation_cannot_be_asked_for_is_reported() {
1829        let text = wrap(
1830            "(ptr) -> i32",
1831            "block0(%0: ptr):\n    %1 = atomic_load.i32 %0, align 4, release\n    return %1\n",
1832        );
1833        assert_eq!(only(&text), "@f block0 atomic_load: atomic_load cannot be asked for release");
1834    }
1835
1836    #[test]
1837    fn an_ordering_on_the_non_atomic_form_is_reported() {
1838        let text = wrap(
1839            "(ptr) -> i32",
1840            "block0(%0: ptr):\n    %1 = load.i32 %0, align 4, acquire\n    return %1\n",
1841        );
1842        assert_eq!(only(&text), "@f block0 load: load cannot be asked for acquire");
1843    }
1844
1845    #[test]
1846    fn a_va_object_that_answers_anything_but_an_address_is_reported() {
1847        // Where the object is, which is the only thing it can answer, since the object it reads
1848        // is an aggregate and an aggregate is not a value the IR has a type for.
1849        let text = wrap(
1850            "(ptr) -> i64",
1851            "block0(%0: ptr):
1852    %1 = va_object.i64 %0, size 16, align 8
1853    return %1
1854",
1855        );
1856        assert_eq!(
1857            only(&text),
1858            "@f block0 va_object: va_object answers where the object is and i64 is not an address"
1859        );
1860    }
1861
1862    #[test]
1863    fn an_alloca_of_a_fixed_size_outside_the_entry_block_is_reported() {
1864        let text = wrap(
1865            "()",
1866            "block0:
1867    jump block1
1868
1869block1:
1870    %0 = alloca, size 16, align 8
1871    return
1872",
1873        );
1874        assert_eq!(
1875            only(&text),
1876            "@f block1 alloca: an alloca of a fixed size belongs in the entry block"
1877        );
1878    }
1879
1880    #[test]
1881    fn a_dynamic_alloca_may_be_anywhere() {
1882        let text = wrap(
1883            "(i64)",
1884            "block0(%0: i64):
1885    jump block1
1886
1887block1:
1888    %1 = alloca %0, align 8
1889    return
1890",
1891        );
1892        assert_eq!(errors(&text), Vec::<String>::new());
1893    }
1894
1895    #[test]
1896    fn two_cases_of_a_switch_with_the_same_value_are_reported() {
1897        let text = wrap(
1898            "(i32)",
1899            "block0(%0: i32):
1900    switch %0, block1, [7 => block1, 7 => block1]
1901
1902block1:
1903    return
1904",
1905        );
1906        assert_eq!(only(&text), "@f block0 switch: two cases of this switch have the same value");
1907    }
1908
1909    #[test]
1910    fn operands_that_do_not_agree_are_reported() {
1911        let text = wrap(
1912            "(i32, i64) -> i32",
1913            "block0(%0: i32, %1: i64):\n    %2 = add %0, %1\n    return %2\n",
1914        );
1915        assert_eq!(
1916            only(&text),
1917            "@f block0 add: the operands of add have one type and these are i32 and i64"
1918        );
1919    }
1920
1921    #[test]
1922    fn a_conversion_that_goes_the_wrong_way_is_reported() {
1923        let text = wrap("(i32) -> i64", "block0(%0: i32):\n    %1 = trunc.i64 %0\n    return %1\n");
1924        assert_eq!(
1925            only(&text),
1926            "@f block0 trunc: trunc produces something narrower and i32 to i64 is not"
1927        );
1928    }
1929
1930    #[test]
1931    fn a_bitcast_between_an_address_and_a_number_is_reported() {
1932        let text =
1933            wrap("(ptr) -> i64", "block0(%0: ptr):\n    %1 = bitcast.i64 %0\n    return %1\n");
1934        assert_eq!(
1935            only(&text),
1936            "@f block0 bitcast: a bitcast between a pointer and a number is ptrtoint or inttoptr"
1937        );
1938    }
1939
1940    #[test]
1941    fn an_operand_of_the_wrong_kind_is_reported() {
1942        let text = wrap("(i32) -> i32", "block0(%0: i32):\n    %1 = fadd %0, %0\n    return %1\n");
1943        assert_eq!(
1944            only(&text),
1945            "@f block0 fadd: operand 1 of fadd is a floating point value and this one is i32"
1946        );
1947    }
1948
1949    #[test]
1950    fn a_condition_that_is_not_one_bit_is_reported() {
1951        let text = wrap(
1952            "(i32)",
1953            "block0(%0: i32):
1954    br_if %0, block1, block1
1955
1956block1:
1957    return
1958",
1959        );
1960        assert_eq!(only(&text), "@f block0 br_if: br_if branches on an i1 and this one on i32");
1961    }
1962
1963    #[test]
1964    fn a_return_that_does_not_match_the_signature_is_reported() {
1965        let text = wrap("() -> i32", "block0:\n    return\n");
1966        assert_eq!(only(&text), "@f block0 return: the signature returns 1 and this returns 0");
1967    }
1968
1969    #[test]
1970    fn a_call_that_disagrees_with_the_declaration_is_reported() {
1971        let text = format!(
1972            "{HEADER}
1973func @g(i32, ...) -> i32, linkage(external);
1974
1975func @f(i32) -> i32, linkage(external) {{
1976block0(%0: i32):
1977    %1 = call @g(%0) : (i32) -> i32
1978    return %1
1979}}
1980"
1981        );
1982        assert_eq!(only(&text), "@f block0 call: @g is declared here with another signature");
1983    }
1984
1985    #[test]
1986    fn a_global_whose_image_is_not_its_size_is_reported() {
1987        let text =
1988            format!("{HEADER}\nglobal @x : bytes 8 = {{ i32 7 }}, align 4, linkage(external)\n");
1989        assert_eq!(only(&text), "@x: the image is 4 bytes and the global is 8");
1990    }
1991
1992    #[test]
1993    fn a_pointer_in_an_image_has_no_width_and_is_reported() {
1994        // What a `NULL` in a static initializer used to become. A `ptr` takes the width of the
1995        // target's addresses and a type says nothing about the target, so the datum measured
1996        // zero bytes and the value it held went nowhere.
1997        let text = format!(
1998            "{HEADER}\nglobal @x : bytes 8 = {{ ptr 0x0, zero 8 }}, align 8, linkage(external)\n"
1999        );
2000        assert_eq!(only(&text), "@x: a scalar in an image has a width and ptr has none");
2001    }
2002
2003    #[test]
2004    fn a_declaration_of_something_another_module_defines_may_be_constant() {
2005        // `extern const int x;` names an object in the library's read only data. Whether it may
2006        // be written through is a fact about the object rather than about who holds the bytes.
2007        let text = format!("{HEADER}\nglobal @x : bytes 4, align 4, linkage(external), constant\n");
2008        assert!(errors(&text).is_empty(), "{:?}", errors(&text));
2009    }
2010
2011    #[test]
2012    fn an_alias_to_itself_is_reported() {
2013        let text = format!("{HEADER}\nalias @a = @a, linkage(external)\n");
2014        assert_eq!(only(&text), "@a: an alias to itself");
2015    }
2016
2017    #[test]
2018    fn an_ifunc_that_does_not_resolve_through_a_function_is_reported() {
2019        let text = format!(
2020            "{HEADER}
2021global @g : i32 = 0, align 4, linkage(external)
2022
2023ifunc @f = @g, linkage(external)
2024"
2025        );
2026        assert_eq!(
2027            only(&text),
2028            "@f: an ifunc resolves through a function and this target is not one"
2029        );
2030    }
2031
2032    #[test]
2033    fn attributes_that_contradict_each_other_are_reported() {
2034        let text =
2035            format!("{HEADER}\nfunc @f(), linkage(external), attrs(always_inline, noinline);\n");
2036        assert_eq!(
2037            only(&text),
2038            "@f: `always_inline` and `noinline` cannot both be true of a function"
2039        );
2040    }
2041
2042    // The rest are things no text can say, because the parser turns them down before the
2043    // verifier would see them. They are what a pass produces, which is who the verifier is for.
2044
2045    fn one_error(module: &Module, func: &Func, names: &Interner) -> String {
2046        match verify_func(module, func, names) {
2047            Ok(()) => panic!("that was expected to be turned down"),
2048            Err(errors) => {
2049                assert_eq!(errors.len(), 1, "{errors:#?}");
2050                errors[0].to_string()
2051            }
2052        }
2053    }
2054
2055    #[test]
2056    fn a_value_the_function_does_not_have_is_reported() {
2057        let mut names = Interner::new();
2058        let module = Module::new(names.intern("built.c"), &target());
2059        let mut func = Func::new(names.intern("f"), Signature::new());
2060        let block = func.create_block();
2061        let args = func.push_values(&[Value::from_usize(9)]);
2062        let inst =
2063            func.create_inst(InstData { args, ..InstData::new(Opcode::Return) }, &[], Span::DUMMY);
2064        func.append_inst(block, inst);
2065        assert_eq!(
2066            one_error(&module, &func, &names),
2067            "@f block0 return: %9 is not a value of this function"
2068        );
2069    }
2070
2071    #[test]
2072    fn a_value_whose_definition_has_been_taken_out_is_reported() {
2073        let mut names = Interner::new();
2074        let module = Module::new(names.intern("built.c"), &target());
2075        let i32_ = Type::int(32);
2076        let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
2077        let block = func.create_block();
2078        let mut b = Builder::new(&mut func, block);
2079        let value = b.iconst(i32_, 7);
2080        b.ret(&[value]);
2081        let Def::Result { inst, .. } = func[value].def else { unreachable!("a constant") };
2082        func.remove_inst(inst);
2083        assert_eq!(
2084            one_error(&module, &func, &names),
2085            "@f block0 return: %0 is produced by an instruction that is not in the function"
2086        );
2087    }
2088
2089    #[test]
2090    fn an_instruction_carrying_another_opcodes_payload_is_reported() {
2091        // This one prints as text the parser cannot read back, so the round trip would fail on
2092        // it somewhere else entirely if the verifier did not say so here.
2093        let mut names = Interner::new();
2094        let module = Module::new(names.intern("built.c"), &target());
2095        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
2096        let block = func.create_block();
2097        let param = func.append_param(block, Type::int(32));
2098        let args = func.push_values(&[param, param]);
2099        let inst = func.create_inst(
2100            InstData { args, extra: Extra::IntPred(IntPred::Eq), ..InstData::new(Opcode::Add) },
2101            &[Type::int(32)],
2102            Span::DUMMY,
2103        );
2104        func.append_inst(block, inst);
2105        let ret = func.create_inst(InstData::new(Opcode::Return), &[], Span::DUMMY);
2106        func.append_inst(block, ret);
2107        assert_eq!(
2108            one_error(&module, &func, &names),
2109            "@f block0 add: add carries nothing and this one carries an integer comparison"
2110        );
2111    }
2112
2113    #[test]
2114    fn a_metadata_node_that_is_its_own_parent_is_reported() {
2115        let mut names = Interner::new();
2116        let mut module = Module::new(names.intern("built.c"), &target());
2117        module.add_meta(MetaNode {
2118            name: names.intern("int"),
2119            parent: Some(Idx::from_usize(0)),
2120            offset: 0,
2121        });
2122        let found = match verify(&module, &names) {
2123            Ok(()) => panic!("that was expected to be turned down"),
2124            Err(errors) => errors,
2125        };
2126        assert_eq!(found.len(), 1, "{found:#?}");
2127        assert_eq!(
2128            found[0].to_string(),
2129            "!0: a metadata node's parent comes before it and this one is !0"
2130        );
2131    }
2132
2133    #[test]
2134    fn a_datalayout_the_target_does_not_imply_is_reported() {
2135        let mut names = Interner::new();
2136        let mut module = Module::new(names.intern("built.c"), &target());
2137        module.datalayout = DataLayout::parse("e-p:32:32-i64:64-f80:32-S64").expect("a layout");
2138        let found = match verify(&module, &names) {
2139            Ok(()) => panic!("that was expected to be turned down"),
2140            Err(errors) => errors,
2141        };
2142        assert_eq!(found.len(), 1, "{found:#?}");
2143        assert!(found[0].to_string().starts_with("@built.c: the datalayout is "), "{}", found[0]);
2144    }
2145
2146    #[test]
2147    fn a_flag_riding_along_where_it_is_read_is_not_reported() {
2148        let mut names = Interner::new();
2149        let module = Module::new(names.intern("built.c"), &target());
2150        let i32_ = Type::int(32);
2151        let mut func = Func::new(
2152            names.intern("f"),
2153            Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
2154        );
2155        let block = func.create_block();
2156        let param = func.append_param(block, i32_);
2157        let mut b = Builder::new(&mut func, block);
2158        let sum = b.binary(Opcode::Add, param, param, Flags::NSW);
2159        b.ret(&[sum]);
2160        assert!(verify_func(&module, &func, &names).is_ok());
2161    }
2162
2163    #[test]
2164    fn a_declaration_is_checked_and_has_nothing_else_to_check() {
2165        let mut names = Interner::new();
2166        let module = Module::new(names.intern("built.c"), &target());
2167        let func = Func::new(Symbol::from_raw(0), Signature::new());
2168        assert!(func.is_declaration());
2169        assert!(verify_func(&module, &func, &names).is_ok());
2170    }
2171}