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