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::{
49    Abi, Block, CallInfo, Def, Inst, MetaNode, Param, PlaneNode, Signature, VaInfo, Value,
50};
51use crate::module::{Alias, AliasKind, DataLayout, Datum, Global, Module, SymbolRef};
52use crate::{Extra, MemOrder, Meta, Opcode, Type};
53
54/// One thing wrong with a module.
55#[derive(Clone, Debug, PartialEq, Eq)]
56pub struct VerifyError {
57    /// What it is about, as `@sum block1 add`, or `@counter` for a global, or `!1` for a
58    /// metadata node.
59    pub at: String,
60    /// What is wrong with it.
61    pub message: String,
62}
63
64impl fmt::Display for VerifyError {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        write!(f, "{}: {}", self.at, self.message)
67    }
68}
69
70impl std::error::Error for VerifyError {}
71
72/// Checks a whole module.
73///
74/// # Errors
75///
76/// Gives back everything wrong with it, in the order the module is walked, which is the globals
77/// then the aliases then the functions then the metadata.
78pub fn verify(module: &Module, names: &Interner) -> Result<(), Vec<VerifyError>> {
79    let mut verifier = Verifier::new(module, names);
80    verifier.module();
81    verifier.finish()
82}
83
84/// Checks one function, and nothing else in the module it is in.
85///
86/// This is what a pass calls after rewriting a function, since walking the whole module after
87/// every function of it would be quadratic.
88///
89/// # Errors
90///
91/// Gives back everything wrong with that function.
92pub fn verify_func<'a>(
93    module: &'a Module,
94    func: &'a Func,
95    names: &'a Interner,
96) -> Result<(), Vec<VerifyError>> {
97    let mut verifier = Verifier::new(module, names);
98    verifier.func(func);
99    verifier.finish()
100}
101
102/// Checking one module.
103struct Verifier<'a> {
104    module: &'a Module,
105    names: &'a Interner,
106    errors: Vec<VerifyError>,
107    /// Where the walk is, which is what an error is labelled with. Built into a string only
108    /// when something is actually wrong, because the common case is that nothing is.
109    func: Option<&'a Func>,
110    block: Option<Block>,
111    inst: Option<Inst>,
112}
113
114impl<'a> Verifier<'a> {
115    fn new(module: &'a Module, names: &'a Interner) -> Self {
116        Verifier { module, names, errors: Vec::new(), func: None, block: None, inst: None }
117    }
118
119    fn finish(self) -> Result<(), Vec<VerifyError>> {
120        if self.errors.is_empty() { Ok(()) } else { Err(self.errors) }
121    }
122
123    // The module.
124
125    fn module(&mut self) {
126        let implied = DataLayout::for_target(&TargetInfo::for_tuple(self.module.tuple));
127        if self.module.datalayout != implied {
128            self.at(
129                format!("@{}", self.names.resolve(self.module.name)),
130                format!(
131                    "the datalayout is `{}` and {} implies `{implied}`",
132                    self.module.datalayout, self.module.tuple
133                ),
134            );
135        }
136
137        for id in self.module.globals() {
138            self.global(&self.module[id]);
139        }
140        for id in self.module.aliases() {
141            self.alias(&self.module[id]);
142        }
143        for id in self.module.funcs() {
144            self.func(&self.module[id]);
145        }
146        self.metadata();
147    }
148
149    fn global(&mut self, global: &Global) {
150        let at = format!("@{}", self.names.resolve(global.name));
151        if !global.align.is_power_of_two() {
152            self.at(
153                at.clone(),
154                format!("an alignment is a power of two and this is {}", global.align),
155            );
156        }
157        // A global with no image is a declaration of something another module defines, and
158        // there is nothing here to check about it. It may be constant: `extern const char
159        // *const sys_errlist[];` is a declaration of an object that lives in the library's
160        // read only data, and whether writing through a pointer to it is undefined is a fact
161        // about the object rather than about which module holds the bytes.
162        let Some(init) = global.init else { return };
163        let mut size = 0;
164        for &datum in &self.module[init] {
165            size += datum.size(self.module);
166            // An image is bytes and a scalar in one has to say how many it takes. `ptr` does
167            // not: the width of an address is the target's and not the type's, which is what
168            // makes a `ptr` here a scalar of no size that silently contributes nothing. An
169            // address in an image is [`Datum::Addr`], and a number the program wrote as one is
170            // the integer it is.
171            if let Datum::Scalar { ty, .. } = datum {
172                if ty.bits() == 0 {
173                    self.at(
174                        at.clone(),
175                        format!("a scalar in an image has a width and {ty} has none"),
176                    );
177                }
178            }
179            if let Datum::Addr(reloc) = datum {
180                let bytes = self.module[reloc].size;
181                if !matches!(bytes, 1 | 2 | 4 | 8) {
182                    self.at(
183                        at.clone(),
184                        format!(
185                            "an address is written as 1, 2, 4 or 8 bytes and this one as {bytes}"
186                        ),
187                    );
188                }
189            }
190        }
191        if size != global.size {
192            self.at(at, format!("the image is {size} bytes and the global is {}", global.size));
193        }
194    }
195
196    fn alias(&mut self, alias: &Alias) {
197        let at = format!("@{}", self.names.resolve(alias.name));
198        if alias.name == alias.target {
199            self.at(at, "an alias to itself");
200            return;
201        }
202        // Only what this module holds is checked. A target defined in another object is what
203        // an alias to a weak symbol looks like, and whether it resolves is the linker's answer.
204        let Some(found) = self.module.lookup(alias.target) else { return };
205        if alias.kind == AliasKind::IFunc && !matches!(found, SymbolRef::Func(_)) {
206            self.at(at, "an ifunc resolves through a function and this target is not one");
207        }
208    }
209
210    fn metadata(&mut self) {
211        for node in self.module.metadata() {
212            let at = format!("!{}", node.raw());
213            if let Some(named) = self.module[node].points_at() {
214                if named.raw() >= node.raw() {
215                    // A parent that comes later cannot be a tree, and a parent that is the node
216                    // itself is the cycle the walk up a TBAA tree would never leave. A plane
217                    // entry names a type rather than a parent and is under the same rule, so
218                    // that the table is read once from the top and nothing in it looks forward.
219                    self.at(
220                        at.clone(),
221                        format!(
222                            "a metadata node names one that comes before it and this one is !{}",
223                            named.raw()
224                        ),
225                    );
226                    continue;
227                }
228                if self.module[node].plane().is_some() && self.module[named].plane().is_some() {
229                    // A plane entry is a type or it is one of the three names, and one that
230                    // named another plane entry would be neither.
231                    self.at(
232                        at.clone(),
233                        format!("a plane entry names a type and !{} is a plane entry", named.raw()),
234                    );
235                }
236            }
237            if let MetaNode::Plane(PlaneNode::PointerSlot(k)) = self.module[node] {
238                // Which byte of a pointer it is, so a byte the target's pointers do not have is
239                // a plane the runtime could never write.
240                let bytes = self.module.datalayout.pointer_bits / 8;
241                if u32::from(k) >= bytes {
242                    self.at(
243                        at,
244                        format!("a pointer on this target is {bytes} bytes and this is byte {k}"),
245                    );
246                }
247            }
248        }
249    }
250
251    // The function.
252
253    fn func(&mut self, func: &'a Func) {
254        self.func = Some(func);
255        self.block = None;
256        self.inst = None;
257
258        if let Some((one, other)) = func.attrs.conflict() {
259            self.error(format!("`{one}` and `{other}` cannot both be true of a function"));
260        }
261        if func.is_declaration() {
262            self.func = None;
263            return;
264        }
265        // Everything after this indexes with values and blocks read out of the function, so
266        // nothing does until they are all known to be in range.
267        if !self.bounds(func) {
268            self.func = None;
269            return;
270        }
271
272        for signature in func.signatures() {
273            self.signature(signature);
274        }
275
276        let entry = func.entry().expect("a function with blocks has a first one");
277        let params: Vec<Type> = func[entry].params.iter().map(|&value| func[value].ty).collect();
278        let want: Vec<Type> = func.signature().param_types().collect();
279        if params != want {
280            self.error(format!(
281                "the entry block takes {} and the signature says {}",
282                types(&params),
283                types(&want)
284            ));
285        }
286
287        let doms = Doms::new(func);
288        let layout = Layout::new(func);
289        for block in func.blocks() {
290            self.block = Some(block);
291            self.block(func, block, &doms, &layout);
292        }
293        self.block = None;
294        self.inst = None;
295        self.mem_chain(func);
296        self.capabilities(func);
297        self.facts(func, &doms, &layout);
298        self.func = None;
299    }
300
301    /// What the ABI asks of a signature's parameters agrees with what they are.
302    ///
303    /// None of this is about the target. Whether a `struct` of twenty four bytes travels as its
304    /// own bytes or as the address of a copy is the classification's answer and this has no
305    /// opinion on it, but a `byval` on something that is not a pointer describes no call on any
306    /// target, and neither does a second `sret`.
307    fn signature(&mut self, signature: &Signature) {
308        for (index, param) in signature.params.iter().enumerate() {
309            let at = format!("parameter {}", index + 1);
310            self.abi(&at, param);
311            match param.abi {
312                Abi::Sret { .. } if index > 0 => {
313                    // It is the address the return value goes to, so it arrives before anything
314                    // the function was called with. A later one is a different calling
315                    // convention wearing the same word.
316                    self.error("sret is the first parameter and this one is not");
317                }
318                Abi::Sret { .. } if !signature.returns.is_empty() => {
319                    self.error("a signature returning through sret returns nothing else");
320                }
321                _ => {}
322            }
323        }
324        for (index, param) in signature.returns.iter().enumerate() {
325            let at = format!("result {}", index + 1);
326            self.abi(&at, param);
327            if param.abi.indirect() {
328                // A return value too large for the registers comes back through an `sret`
329                // parameter, which is a parameter and is checked as one.
330                self.error(format!("{at} travels indirectly and a result cannot"));
331            }
332        }
333    }
334
335    /// What a call says about the arguments its signature does not name.
336    ///
337    /// The list is empty when they all travel as the values in hand, so what is checked is the
338    /// other case: there is one entry for each of them, the call is variadic because nothing
339    /// else has such an argument, and each entry describes something an argument can do. An
340    /// `sret` cannot be one of them, since the space for a return value arrives first and the
341    /// first argument is one the signature names.
342    fn varargs(
343        &mut self,
344        func: &'a Func,
345        info: &CallInfo,
346        passed: usize,
347        arg: impl Fn(usize) -> Type,
348    ) {
349        let varargs = &func[info.varargs];
350        if varargs.is_empty() {
351            return;
352        }
353        let named = func[info.signature].params.len();
354        if !func[info.signature].variadic {
355            self.error("this call is not variadic and says how a variadic argument travels");
356            return;
357        }
358        if varargs.len() != passed.saturating_sub(named) {
359            self.error(format!(
360                "the call passes {} arguments the signature does not name and says how {} travel",
361                passed.saturating_sub(named),
362                varargs.len()
363            ));
364            return;
365        }
366        for (index, &abi) in varargs.iter().enumerate() {
367            let at = format!("argument {}", named + index + 1);
368            if matches!(abi, Abi::Sret { .. }) {
369                self.error(format!("{at} is an sret and only a parameter can be one"));
370                continue;
371            }
372            self.abi(&at, &Param { ty: arg(index + named), abi });
373        }
374    }
375
376    /// One parameter's attribute against its type.
377    fn abi(&mut self, at: &str, param: &Param) {
378        if param.ty.is_mem() {
379            // Memory is a value inside a function and it stops at the call. What a call does to
380            // it is what the callee's attributes say, per document 08.4, and a function that
381            // passed a version of memory to another would be claiming something else.
382            self.error(format!("{at} is memory, and no function takes or returns memory"));
383        }
384        if param.ty.is_cap() {
385            // Not a rule about this ABI or that one. A capability is metadata the instrumentation
386            // keeps beside a pointer, per `spec/safe-memory/05-representation.md` section 5.2, and
387            // a signature that carried one would be a calling convention only instrumented code
388            // could use, which is the thing that specification spends document 10 avoiding.
389            self.error(format!("{at} is a capability and a capability does not cross a call"));
390        }
391        match param.abi {
392            Abi::Plain => {}
393            Abi::Sext | Abi::Zext => {
394                if !param.ty.is_int() || param.ty.is_vector() {
395                    self.error(format!("{at} is extended and {} is not an integer", param.ty));
396                }
397            }
398            Abi::ByVal { size, align } | Abi::Sret { size, align } => {
399                if !param.ty.is_ptr() {
400                    self.error(format!(
401                        "{at} travels indirectly and {} is not a pointer",
402                        param.ty
403                    ));
404                }
405                if !align.is_power_of_two() {
406                    self.error(format!("an alignment is a power of two and this is {align}"));
407                }
408                if size == 0 {
409                    self.error(format!("{at} travels indirectly and has no size"));
410                }
411            }
412        }
413    }
414
415    /// Every value and every block an instruction names is one the function has.
416    ///
417    /// This is separate and comes first because everything else reads a `ValueData` or a
418    /// `BlockData` out of a table, and an index past the end of one is a panic rather than a
419    /// diagnosis.
420    fn bounds(&mut self, func: &'a Func) -> bool {
421        let counts = func.counts();
422        let before = self.errors.len();
423        for block in func.blocks() {
424            self.block = Some(block);
425            for &value in &func[block].params {
426                if value.index() >= counts.values {
427                    self.error(format!(
428                        "parameter %{} is not a value of this function",
429                        value.raw()
430                    ));
431                }
432            }
433            for inst in func.insts(block) {
434                self.inst = Some(inst);
435                for &value in &func[func[inst].args] {
436                    if value.index() >= counts.values {
437                        self.error(format!("%{} is not a value of this function", value.raw()));
438                    }
439                }
440                for value in func[inst].results() {
441                    if value.index() >= counts.values {
442                        self.error(format!("%{} is not a value of this function", value.raw()));
443                    }
444                }
445                for call in func.successors(inst) {
446                    if call.block.index() >= counts.blocks {
447                        self.error(format!(
448                            "block{} is not a block of this function",
449                            call.block.raw()
450                        ));
451                    }
452                    for &value in &func[call.args] {
453                        if value.index() >= counts.values {
454                            self.error(format!("%{} is not a value of this function", value.raw()));
455                        }
456                    }
457                }
458            }
459            self.inst = None;
460        }
461        self.block = None;
462        self.errors.len() == before
463    }
464
465    fn block(&mut self, func: &'a Func, block: Block, doms: &Doms, layout: &Layout) {
466        if !doms.reaches(block) {
467            self.error("this block is not reachable and has not been deleted");
468        }
469        if block == func.entry().expect("checked in func") {
470            for other in func.blocks() {
471                let last = func[other].last;
472                if last.is_some_and(|inst| func.successors(inst).any(|call| call.block == block)) {
473                    self.error("the entry block is branched to, and it takes the arguments");
474                }
475            }
476        }
477
478        let mut seen_terminator = false;
479        for inst in func.insts(block) {
480            self.inst = Some(inst);
481            if seen_terminator {
482                self.error("this comes after the block's terminator");
483            }
484            seen_terminator |= func.is_terminator(inst);
485            self.inst(func, inst, doms, layout);
486        }
487        self.inst = None;
488        if !seen_terminator {
489            self.error("this block does not end in a terminator");
490        }
491    }
492
493    fn inst(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
494        let data = &func[inst];
495        let opcode = data.opcode;
496
497        let stray = data.flags.without(crate::Flags::legal_on(opcode));
498        if !stray.is_empty() {
499            let names: Vec<&str> = stray.iter().map(|(_, name)| name).collect();
500            self.error(format!("{} does not read `{}`", opcode.name(), names.join("`, `")));
501        }
502        if data.extra.kind() != opcode.extra_kind() {
503            // An instruction carrying some other opcode's payload prints as text the parser
504            // cannot read, so this is caught here rather than found as a round trip failure.
505            self.error(format!(
506                "{} carries {} and this one carries {}",
507                opcode.name(),
508                opcode.extra_kind().name(),
509                data.extra.kind().name()
510            ));
511        }
512        if let Some(want) = opcode.results() {
513            // An instruction that has been threaded onto the memory chain produces one more
514            // than its opcode says, which is the new version of memory. A `mem_entry` is the
515            // one whose memory is not extra, because memory is all it produces.
516            let want = want + u8::from(extra_mem(func, inst));
517            if want != data.results {
518                self.error(format!(
519                    "{} produces {want} values and this one produces {}",
520                    opcode.name(),
521                    data.results
522                ));
523            }
524        }
525
526        if let Extra::Node(at) = data.extra {
527            if at.index() >= self.module.counts().metadata {
528                self.error(format!("!{} is not a metadata node of this module", at.raw()));
529            } else {
530                self.names_the_right_node(opcode, at);
531            }
532        }
533
534        self.uses(func, inst, doms, layout);
535        self.branches(func, inst);
536        self.memory(func, inst);
537        self.mem_ssa(func, inst);
538        self.shape(func, inst);
539    }
540
541    /// Every use is reached by its definition.
542    fn uses(&mut self, func: &'a Func, inst: Inst, doms: &Doms, layout: &Layout) {
543        let block = layout.block_of(inst).expect("walking the blocks");
544        let check = |verifier: &mut Self, value: Value| match func[value].def {
545            Def::Param { block: def, .. } => {
546                if !doms.dominates(def, block) {
547                    verifier.error(format!(
548                        "%{} arrives at block{} and does not reach here",
549                        value.raw(),
550                        def.raw()
551                    ));
552                }
553            }
554            Def::Result { inst: def, .. } => {
555                let Some(def_block) = layout.block_of(def) else {
556                    verifier.error(format!(
557                        "%{} is produced by an instruction that is not in the function",
558                        value.raw()
559                    ));
560                    return;
561                };
562                let reaches = if def_block == block {
563                    layout.position(def) < layout.position(inst)
564                } else {
565                    doms.dominates(def_block, block)
566                };
567                if !reaches {
568                    verifier.error(format!(
569                        "%{} is produced in block{} and does not reach here",
570                        value.raw(),
571                        def_block.raw()
572                    ));
573                }
574            }
575        };
576        for &value in &func[func[inst].args] {
577            check(self, value);
578        }
579        for call in func.successors(inst) {
580            for &value in &func[call.args] {
581                check(self, value);
582            }
583        }
584    }
585
586    /// Every branch passes what the block it goes to takes.
587    fn branches(&mut self, func: &'a Func, inst: Inst) {
588        if func[inst].opcode == Opcode::BlockAddr {
589            // The one instruction that names a block without arriving at it, so the block's
590            // parameters are nothing to do with it. That it passes no arguments is checked
591            // with the rest of its shape.
592            return;
593        }
594        for call in func.successors(inst) {
595            let params = &func[call.block].params;
596            let args = &func[call.args];
597            if params.len() != args.len() {
598                self.error(format!(
599                    "block{} takes {} arguments and this branch passes {}",
600                    call.block.raw(),
601                    params.len(),
602                    args.len()
603                ));
604                continue;
605            }
606            for (index, (&param, &arg)) in params.iter().zip(args).enumerate() {
607                let (want, got) = (func[param].ty, func[arg].ty);
608                if want != got {
609                    self.error(format!(
610                        "argument {} to block{} is {want} and this one is {got}",
611                        index + 1,
612                        call.block.raw()
613                    ));
614                }
615            }
616        }
617        if let Extra::Switch(info) = func[inst].extra {
618            let switch = &func[info];
619            let targets = &func[switch.targets];
620            let cases = &func[switch.cases];
621            if targets.len() != cases.len() + 1 {
622                self.error(format!(
623                    "a switch has one target per case and a default, and this one has {} targets for {} cases",
624                    targets.len(),
625                    cases.len()
626                ));
627            }
628            for (index, case) in cases.iter().enumerate() {
629                if cases[..index].contains(case) {
630                    self.error("two cases of this switch have the same value");
631                }
632            }
633        }
634    }
635
636    /// Whether a node reference is to the kind of node the instruction reads.
637    ///
638    /// The two type plane instructions read a plane entry and everything else reads an aliasing
639    /// node. They share the field because they share the table, and an instruction handed the
640    /// wrong kind would either ask the aliasing tree a question about `no_type` or walk a plane
641    /// entry as though it had a parent.
642    fn names_the_right_node(&mut self, opcode: Opcode, at: Meta) {
643        let wants_plane = matches!(opcode, Opcode::CheckType | Opcode::MetaType);
644        let is_plane = self.module[at].plane().is_some();
645        if wants_plane != is_plane {
646            let (asked, got) = if wants_plane {
647                ("a plane entry", "an aliasing node")
648            } else {
649                ("an aliasing node", "a plane entry")
650            };
651            self.error(format!("{} names {asked} and !{} is {got}", opcode.name(), at.raw()));
652        }
653    }
654
655    /// What an access says about itself.
656    fn memory(&mut self, func: &'a Func, inst: Inst) {
657        let opcode = func[inst].opcode;
658        let info = match func[inst].extra {
659            Extra::Mem(at) => func[at],
660            Extra::Rmw(_, at) => func[at],
661            Extra::VaObject(at) => {
662                let object = func[at];
663                self.slots(func, object);
664                func[object.mem]
665            }
666            Extra::Order(order) => {
667                if !order.is_valid_for_rmw() {
668                    self.error("a fence is not a fence unless it orders something");
669                }
670                return;
671            }
672            _ => return,
673        };
674        if !info.align.is_power_of_two() {
675            self.error(format!("an alignment is a power of two and this is {}", info.align));
676        }
677        if let Some(tbaa) = info.tbaa {
678            if tbaa.index() >= self.module.counts().metadata {
679                self.error(format!("!{} is not a metadata node of this module", tbaa.raw()));
680            } else {
681                self.names_the_right_node(opcode, tbaa);
682            }
683        }
684        if info.owns != 0 && !matches!(opcode, Opcode::Store | Opcode::AtomicStore) {
685            // Only a store records anything into the init plane, so the number is either on the
686            // wrong instruction or means something the reader of it will not do.
687            self.error(format!(
688                "a {} owns no padding, since only a store records any",
689                opcode.name()
690            ));
691        }
692        if info.restrict.clique == 0 && info.restrict.base != 0 {
693            // A base outside any scope disambiguates nothing and reads as though it did, so it
694            // is either a lowering bug or a hand-written function that means something else.
695            self.error(format!(
696                "restrict base {} is in no clique, and a base means nothing without one",
697                info.restrict.base
698            ));
699        }
700        let ok = match opcode {
701            Opcode::AtomicLoad => info.order.is_valid_for_load(),
702            Opcode::AtomicStore => info.order.is_valid_for_store(),
703            Opcode::AtomicRmw | Opcode::Cmpxchg => info.order.is_valid_for_rmw(),
704            // Everything else is the non-atomic form, and the atomic form is a different
705            // opcode, so an ordering here is one somebody meant to put on that one.
706            _ => info.order == MemOrder::NotAtomic,
707        };
708        if !ok {
709            self.error(format!("{} cannot be asked for {}", opcode.name(), info.order));
710        }
711        if matches!(opcode, Opcode::Memcpy | Opcode::Memmove | Opcode::Memset) && info.size == 0 {
712            self.error(format!("{} moves no bytes", opcode.name()));
713        }
714    }
715
716    /// The memory chain, where the function has one.
717    ///
718    /// Design: `spec/optimizer/09-memory-ssa.md`. Memory is a value of type `mem`, it arrives on
719    /// an instruction as the last operand and leaves as the last result, and a memory phi is an
720    /// ordinary parameter of a block. That is the whole representation, and these are the rules
721    /// that make it mean what section 9.6 says it means.
722    fn mem_ssa(&mut self, func: &'a Func, inst: Inst) {
723        let opcode = func[inst].opcode;
724        let takes = func.mem_in(inst).is_some();
725        let gives = func.mem_out(inst).is_some();
726
727        if takes && !opcode.touches_memory() {
728            // Threading an instruction that does not read or write memory onto the chain puts a
729            // barrier where there is nothing to order, and every walk past it stops early.
730            self.error(format!(
731                "{} does not touch memory and is on the memory chain",
732                opcode.name()
733            ));
734        }
735        if gives && !opcode.writes_memory() && opcode != Opcode::MemEntry {
736            self.error(format!(
737                "{} does not write memory and produces a new version of it",
738                opcode.name()
739            ));
740        }
741        if opcode.writes_memory() && takes != gives {
742            // A write is where one version of memory ends and the next begins, so it is on the
743            // chain at both ends or at neither. Half of it is a chain with a hole in it.
744            self.error(format!(
745                "{} takes {} and produces {}, and a write does both or neither",
746                opcode.name(),
747                if takes { "memory" } else { "no memory" },
748                if gives { "a new version" } else { "none" }
749            ));
750        }
751
752        // Memory goes to the next instruction on the chain and to the blocks a branch arrives
753        // at, and nowhere else. Anything else reading one is arithmetic on a thing that has no
754        // bits, and it would keep a version alive somewhere the walk does not look.
755        let args = &func[func[inst].args];
756        let ordinary = args.len() - usize::from(takes);
757        for (index, &arg) in args[..ordinary].iter().enumerate() {
758            if func[arg].ty.is_mem() {
759                self.error(format!(
760                    "%{} is memory and operand {} of {} is not the memory operand",
761                    arg.raw(),
762                    index + 1,
763                    opcode.name()
764                ));
765            }
766        }
767    }
768
769    /// The memory chain over a whole function, which is the part no single instruction can see.
770    ///
771    /// Memory is all or nothing. Either every instruction that touches it is on the chain or none
772    /// of them is, because a walk that reaches an instruction with no memory operand cannot tell
773    /// whether that instruction was left off the chain or does not belong on it, and the honest
774    /// answer at that point is that the chain says nothing about this function at all. Section 9.6
775    /// gives this as the main argument for the explicit operand over a side table: a side table
776    /// has no place to write down that it is incomplete, and this does.
777    fn mem_chain(&mut self, func: &'a Func) {
778        let mut entries = Vec::new();
779        let mut on = Vec::new();
780        let mut off = Vec::new();
781        for block in func.blocks() {
782            for inst in func.insts(block) {
783                if func[inst].opcode == Opcode::MemEntry {
784                    entries.push((block, inst));
785                } else if func[inst].opcode.touches_memory() {
786                    if func.carries_mem(inst) { &mut on } else { &mut off }.push((block, inst));
787                }
788            }
789        }
790
791        if let Some(&(block, inst)) = entries.get(1) {
792            self.block = Some(block);
793            self.inst = Some(inst);
794            self.error("a function starts with one version of memory and this is a second one");
795        }
796        if on.is_empty() {
797            if let Some(&(block, inst)) = entries.first() {
798                self.block = Some(block);
799                self.inst = Some(inst);
800                self.error("nothing in this function is on the memory chain, and this starts one");
801            }
802        } else {
803            if entries.is_empty() {
804                let (block, inst) = on[0];
805                self.block = Some(block);
806                self.inst = Some(inst);
807                self.error("this is on the memory chain and the function has no mem_entry");
808            }
809            if let Some(&(block, inst)) = off.first() {
810                self.block = Some(block);
811                self.inst = Some(inst);
812                self.error(format!(
813                    "{} touches memory and is not on the chain, and the rest of this function is",
814                    func[inst].opcode.name()
815                ));
816            }
817        }
818        self.block = None;
819        self.inst = None;
820    }
821
822    /// Where a capability comes from.
823    ///
824    /// Six instructions make one, per `spec/safe-memory/06-instrumentation.md` section 6.2.2, and
825    /// nothing else may. That is the whole rule and it is worth stating separately from the shape
826    /// rules because it is about the type rather than about any one opcode: a `bitcast` that named
827    /// `cap` as its result, or a `load` of one, would be a way to conjure a capability out of a
828    /// value that has none of the provenance a capability stands for, and the checks downstream
829    /// would then be comparing an address against bounds nobody established.
830    ///
831    /// A block parameter may be one. Two paths that each hold a capability meet at a block and the
832    /// value that arrives is a capability, which is what block parameters are for, and the
833    /// instruction on each path is still one of the six.
834    fn capabilities(&mut self, func: &'a Func) {
835        for block in func.blocks() {
836            self.block = Some(block);
837            for inst in func.insts(block) {
838                self.inst = Some(inst);
839                if func[inst].opcode.makes_capability() {
840                    continue;
841                }
842                for value in func[inst].results() {
843                    if func[value].ty.is_cap() {
844                        self.error(format!(
845                            "{} produces a capability and only the cap instructions do that",
846                            func[inst].opcode.name()
847                        ));
848                    }
849                }
850            }
851            self.inst = None;
852        }
853        self.block = None;
854    }
855
856    /// What is claimed about a value, per `spec/safe-memory/06-instrumentation.md` section 6.2.3.
857    ///
858    /// A fact is a promise the optimizer is allowed to act on without checking, so a wrong one is
859    /// not a slow program but a silently unsafe one. Nothing here can tell whether a fact is true,
860    /// which is the pass that established it doing its job, but the shape of one is checkable and
861    /// a fact whose shape is wrong is a bug in that pass rather than in the program.
862    ///
863    /// The two values a `!bounds` names have to reach the value it is about, everywhere that value
864    /// does. A range whose start is computed after the pointer it is the range of is a range no
865    /// consumer of the fact could name, and the rule that gets that right at every use is that the
866    /// two dominate the definition.
867    fn facts(&mut self, func: &'a Func, doms: &Doms, layout: &Layout) {
868        for (value, facts) in func.known() {
869            let ty = func[value].ty;
870            if !ty.is_ptr() {
871                self.error(format!("%{} is said to be a pointer and it is {ty}", value.raw()));
872            }
873            match facts.align {
874                Some(align) if !align.is_power_of_two() => self.error(format!(
875                    "%{} is said to be aligned to {align} and an alignment is a power of two",
876                    value.raw()
877                )),
878                _ => {}
879            }
880            let Some(bounds) = facts.bounds else { continue };
881            let lo = func[bounds.lo].ty;
882            if !lo.is_ptr() {
883                self.error(format!(
884                    "the range %{} is in starts at a pointer and %{} is {lo}",
885                    value.raw(),
886                    bounds.lo.raw()
887                ));
888            }
889            let ext = func[bounds.ext].ty;
890            if !ext.lane().is_int() {
891                self.error(format!(
892                    "the range %{} is in has an integer extent and %{} is {ext}",
893                    value.raw(),
894                    bounds.ext.raw()
895                ));
896            }
897            for named in [bounds.lo, bounds.ext] {
898                if !self.reaches(func, doms, layout, named, value) {
899                    self.error(format!(
900                        "%{} names %{} and does not reach it",
901                        value.raw(),
902                        named.raw()
903                    ));
904                }
905            }
906        }
907    }
908
909    /// Whether one value is defined everywhere another one is.
910    ///
911    /// Which for a block parameter is everywhere its block is reached from, and for a result is
912    /// everywhere after the instruction that produced it.
913    fn reaches(
914        &self,
915        func: &'a Func,
916        doms: &Doms,
917        layout: &Layout,
918        value: Value,
919        at: Value,
920    ) -> bool {
921        let point = |of: Value| match func[of].def {
922            Def::Param { block, .. } => Some((block, None)),
923            Def::Result { inst, .. } => layout.block_of(inst).map(|block| (block, Some(inst))),
924        };
925        let (Some((from, def)), Some((to, use_at))) = (point(value), point(at)) else {
926            // One of them is produced by an instruction that is not in the function, which the
927            // walk over the blocks has already reported.
928            return true;
929        };
930        if from != to {
931            return doms.dominates(from, to);
932        }
933        match (def, use_at) {
934            (None, _) => true,
935            (Some(_), None) => false,
936            (Some(def), Some(use_at)) => layout.position(def) < layout.position(use_at),
937        }
938    }
939
940    /// Where an object read off a variable argument list travelled.
941    ///
942    /// Each slot holds bytes of the object, so a slot that reaches past the end of it is a
943    /// classification that does not belong to this object and the backend would read memory the
944    /// object never had.
945    fn slots(&mut self, func: &'a Func, object: VaInfo) {
946        let size = func[object.mem].size;
947        for &slot in &func[object.slots] {
948            let width = match slot {
949                Slot::Integer { size, .. } => u64::from(size),
950                Slot::Float { format, .. } => u64::from(format.width()).div_ceil(8),
951            };
952            if slot.offset() + width > size {
953                self.error(format!(
954                    "a slot holds bytes {} to {} of an object of {size} bytes",
955                    slot.offset(),
956                    slot.offset() + width
957                ));
958            }
959        }
960    }
961
962    /// The operand and result types, by the rules of section 8.2.
963    ///
964    /// The shape of each group is written out rather than derived from a table, because the
965    /// groups do not have the same shape as each other and a table general enough to hold all
966    /// of them would be harder to read than this.
967    #[expect(clippy::too_many_lines, reason = "one arm per group of opcodes, and they differ")]
968    fn shape(&mut self, func: &'a Func, inst: Inst) {
969        let data = &func[inst];
970        let opcode = data.opcode;
971        // Memory is the last operand and the last result when the function carries it, so
972        // taking the two of them off here leaves every arity and every result count below
973        // saying what the opcode says, which is the point of putting them last.
974        let all = &func[data.args];
975        let args = &all[..all.len() - usize::from(func.mem_in(inst).is_some())];
976        let arity = args.len();
977        let arg = |n: usize| func[args[n]].ty;
978        let results = usize::from(data.results) - usize::from(extra_mem(func, inst));
979        let res = |n: usize| func[data.results().nth(n).expect("within the count")].ty;
980
981        match opcode {
982            // Constants take nothing and say their own type.
983            Opcode::IConst => {
984                if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_int() {
985                    self.error(format!(
986                        "iconst produces an integer and this one produces {}",
987                        res(0)
988                    ));
989                }
990            }
991            Opcode::FConst => {
992                if self.takes(opcode, arity, 0) && results == 1 && !res(0).lane().is_float() {
993                    self.error(format!(
994                        "fconst produces a floating point value and this one produces {}",
995                        res(0)
996                    ));
997                }
998            }
999            Opcode::Splat => {
1000                if self.takes(opcode, arity, 0) && results == 1 && !res(0).is_vector() {
1001                    self.error(format!("splat produces a vector and this one produces {}", res(0)));
1002                }
1003            }
1004            Opcode::GlobalAddr
1005            | Opcode::StackSave
1006            | Opcode::FrameAddress
1007            | Opcode::ReturnAddress => {
1008                if results == 1 && !res(0).is_ptr() {
1009                    self.error(format!(
1010                        "{} produces a pointer and this one produces {}",
1011                        opcode.name(),
1012                        res(0)
1013                    ));
1014                }
1015            }
1016
1017            // Integer arithmetic: two of one type, and that type back.
1018            Opcode::Add
1019            | Opcode::Sub
1020            | Opcode::Mul
1021            | Opcode::SDiv
1022            | Opcode::UDiv
1023            | Opcode::SRem
1024            | Opcode::URem
1025            | Opcode::And
1026            | Opcode::Or
1027            | Opcode::Xor
1028            | Opcode::Shl
1029            | Opcode::LShr
1030            | Opcode::AShr => {
1031                if self.takes(opcode, arity, 2) {
1032                    self.integer(opcode, arg(0), 0);
1033                    self.agree(opcode, arg(0), arg(1));
1034                    if results == 1 {
1035                        self.produces(opcode, res(0), arg(0));
1036                    }
1037                }
1038            }
1039
1040            // Floating point arithmetic, the same shape with one more operand for `fma`.
1041            Opcode::FAdd | Opcode::FSub | Opcode::FMul | Opcode::FDiv | Opcode::FRem => {
1042                if self.takes(opcode, arity, 2) {
1043                    self.floating(opcode, arg(0), 0);
1044                    self.agree(opcode, arg(0), arg(1));
1045                    if results == 1 {
1046                        self.produces(opcode, res(0), arg(0));
1047                    }
1048                }
1049            }
1050            Opcode::FNeg => {
1051                if self.takes(opcode, arity, 1) {
1052                    self.floating(opcode, arg(0), 0);
1053                    if results == 1 {
1054                        self.produces(opcode, res(0), arg(0));
1055                    }
1056                }
1057            }
1058            Opcode::Fma => {
1059                if self.takes(opcode, arity, 3) {
1060                    self.floating(opcode, arg(0), 0);
1061                    self.agree(opcode, arg(0), arg(1));
1062                    self.agree(opcode, arg(0), arg(2));
1063                    if results == 1 {
1064                        self.produces(opcode, res(0), arg(0));
1065                    }
1066                }
1067            }
1068
1069            // A choice between two of the same thing, made by one bit per lane. The condition is
1070            // where the lane counts have to be checked rather than assumed: a scalar bit choosing
1071            // between two vectors is a real shape and so is a vector of bits choosing lane by lane,
1072            // and the second is the one the two arms have to agree with.
1073            Opcode::Select => {
1074                if self.takes(opcode, arity, 3) {
1075                    if arg(0).lane() != Type::I1 {
1076                        self.error(format!(
1077                            "operand 1 of select is the bit that chooses and is i1 or a vector of \
1078                             i1, and this one is {}",
1079                            arg(0)
1080                        ));
1081                    } else if arg(0).lanes() != 1 && arg(0).lanes() != arg(1).lanes() {
1082                        self.error(format!(
1083                            "operand 1 of select chooses one lane at a time, so it has {} lanes or \
1084                             one, and this one has {}",
1085                            arg(1).lanes(),
1086                            arg(0).lanes()
1087                        ));
1088                    }
1089                    self.agree(opcode, arg(1), arg(2));
1090                    if results == 1 {
1091                        self.produces(opcode, res(0), arg(1));
1092                    }
1093                }
1094            }
1095
1096            // A comparison answers one bit per lane, whatever it compared.
1097            Opcode::ICmp | Opcode::FCmp => {
1098                if self.takes(opcode, arity, 2) {
1099                    if opcode == Opcode::FCmp {
1100                        self.floating(opcode, arg(0), 0);
1101                    } else if !arg(0).lane().is_int() && !arg(0).is_ptr() {
1102                        self.error(format!(
1103                            "operand 1 of icmp is an integer or a pointer and this one is {}",
1104                            arg(0)
1105                        ));
1106                    }
1107                    self.agree(opcode, arg(0), arg(1));
1108                    if results == 1 {
1109                        self.produces(opcode, res(0), arg(0).with_lane(Type::I1));
1110                    }
1111                }
1112            }
1113
1114            // Conversions, each of which says which way it goes and by how much.
1115            Opcode::Trunc | Opcode::SExt | Opcode::ZExt => {
1116                if self.takes(opcode, arity, 1) && results == 1 {
1117                    self.integer(opcode, arg(0), 0);
1118                    self.lanes(opcode, res(0), arg(0));
1119                    self.widens(opcode, res(0), arg(0), opcode != Opcode::Trunc);
1120                }
1121            }
1122            Opcode::FPTrunc | Opcode::FPExt => {
1123                if self.takes(opcode, arity, 1) && results == 1 {
1124                    self.floating(opcode, arg(0), 0);
1125                    self.lanes(opcode, res(0), arg(0));
1126                    self.widens(opcode, res(0), arg(0), opcode == Opcode::FPExt);
1127                }
1128            }
1129            Opcode::FPToSI | Opcode::FPToUI => {
1130                if self.takes(opcode, arity, 1) && results == 1 {
1131                    self.floating(opcode, arg(0), 0);
1132                    self.lanes(opcode, res(0), arg(0));
1133                    if !res(0).lane().is_int() {
1134                        self.error(format!(
1135                            "{} produces an integer and this one produces {}",
1136                            opcode.name(),
1137                            res(0)
1138                        ));
1139                    }
1140                }
1141            }
1142            Opcode::SIToFP | Opcode::UIToFP => {
1143                if self.takes(opcode, arity, 1) && results == 1 {
1144                    self.integer(opcode, arg(0), 0);
1145                    self.lanes(opcode, res(0), arg(0));
1146                    if !res(0).lane().is_float() {
1147                        self.error(format!(
1148                            "{} produces a floating point value and this one produces {}",
1149                            opcode.name(),
1150                            res(0)
1151                        ));
1152                    }
1153                }
1154            }
1155            Opcode::PtrToInt => {
1156                if self.takes(opcode, arity, 1) && results == 1 {
1157                    self.pointer(opcode, arg(0), 0);
1158                    self.integer(opcode, res(0), 0);
1159                }
1160            }
1161            Opcode::IntToPtr => {
1162                if self.takes(opcode, arity, 1) && results == 1 {
1163                    self.integer(opcode, arg(0), 0);
1164                    if !res(0).is_ptr() {
1165                        self.error(format!(
1166                            "inttoptr produces a pointer and this one produces {}",
1167                            res(0)
1168                        ));
1169                    }
1170                }
1171            }
1172            Opcode::Bitcast => {
1173                if self.takes(opcode, arity, 1) && results == 1 {
1174                    let (from, to) = (arg(0), res(0));
1175                    if from.is_ptr() != to.is_ptr() {
1176                        // Between an address and a number there is a conversion of its own,
1177                        // and using this one would hide it from anything looking for one.
1178                        self.error(
1179                            "a bitcast between a pointer and a number is ptrtoint or inttoptr",
1180                        );
1181                    } else if width(from) != width(to) {
1182                        self.error(format!("a bitcast keeps the width and {from} and {to} differ"));
1183                    }
1184                }
1185            }
1186
1187            // Memory.
1188            // Memory as the function found it. It takes nothing, produces one `mem`, and
1189            // belongs where the function starts, because a version of memory from further in
1190            // would be one that instructions before it could not have.
1191            Opcode::MemEntry => {
1192                if self.takes(opcode, arity, 0) && results == 1 && !res(0).is_mem() {
1193                    self.error(format!(
1194                        "mem_entry produces memory and this one produces {}",
1195                        res(0)
1196                    ));
1197                }
1198                if func.block_of(inst) != func.entry() {
1199                    self.error("mem_entry belongs in the entry block");
1200                }
1201            }
1202            Opcode::Alloca => {
1203                if arity > 1 {
1204                    self.takes(opcode, arity, 1);
1205                } else if arity == 1 {
1206                    self.integer(opcode, arg(0), 0);
1207                } else if func.block_of(inst) != func.entry() {
1208                    // One of a fixed size in a loop is a stack that grows every time round,
1209                    // which is what the dynamic form asks for explicitly.
1210                    self.error("an alloca of a fixed size belongs in the entry block");
1211                }
1212                if results == 1 && !res(0).is_ptr() {
1213                    self.error(format!(
1214                        "alloca produces a pointer and this one produces {}",
1215                        res(0)
1216                    ));
1217                }
1218            }
1219            Opcode::Load | Opcode::AtomicLoad => {
1220                if self.takes(opcode, arity, 1) {
1221                    self.pointer(opcode, arg(0), 0);
1222                }
1223                if results == 1 && res(0).is_void() {
1224                    self.error(format!("{} reads a value and void is not one", opcode.name()));
1225                }
1226            }
1227            Opcode::Store | Opcode::AtomicStore => {
1228                if self.takes(opcode, arity, 2) {
1229                    if arg(0).is_void() {
1230                        self.error(format!("{} writes a value and void is not one", opcode.name()));
1231                    }
1232                    self.pointer(opcode, arg(1), 1);
1233                }
1234            }
1235            Opcode::PtrAdd => {
1236                if self.takes(opcode, arity, 2) {
1237                    self.pointer(opcode, arg(0), 0);
1238                    self.integer(opcode, arg(1), 1);
1239                    if results == 1 && !res(0).is_ptr() {
1240                        self.error(format!(
1241                            "ptr_add produces a pointer and this one produces {}",
1242                            res(0)
1243                        ));
1244                    }
1245                }
1246            }
1247            Opcode::Memcpy | Opcode::Memmove => {
1248                if self.takes(opcode, arity, 2) {
1249                    self.pointer(opcode, arg(0), 0);
1250                    self.pointer(opcode, arg(1), 1);
1251                }
1252            }
1253            Opcode::Memset => {
1254                if self.takes(opcode, arity, 2) {
1255                    self.pointer(opcode, arg(0), 0);
1256                    self.integer(opcode, arg(1), 1);
1257                }
1258            }
1259            Opcode::AtomicRmw => {
1260                if self.takes(opcode, arity, 2) {
1261                    self.pointer(opcode, arg(0), 0);
1262                    self.integer(opcode, arg(1), 1);
1263                    if results == 1 {
1264                        self.produces(opcode, res(0), arg(1));
1265                    }
1266                }
1267            }
1268            Opcode::Cmpxchg => {
1269                if self.takes(opcode, arity, 3) {
1270                    self.pointer(opcode, arg(0), 0);
1271                    self.agree(opcode, arg(1), arg(2));
1272                    if results == 2 {
1273                        self.produces(opcode, res(0), arg(1));
1274                        self.produces(opcode, res(1), arg(1).with_lane(Type::I1));
1275                    }
1276                }
1277            }
1278            Opcode::Fence | Opcode::Unreachable | Opcode::UnreachableHint => {
1279                self.takes(opcode, arity, 0);
1280            }
1281            Opcode::Prefetch | Opcode::StackRestore | Opcode::VaStart | Opcode::VaEnd => {
1282                if self.takes(opcode, arity, 1) {
1283                    self.pointer(opcode, arg(0), 0);
1284                }
1285            }
1286            Opcode::VaCopy => {
1287                if self.takes(opcode, arity, 2) {
1288                    self.pointer(opcode, arg(0), 0);
1289                    self.pointer(opcode, arg(1), 1);
1290                }
1291            }
1292            Opcode::VaArg => {
1293                if self.takes(opcode, arity, 1) {
1294                    self.pointer(opcode, arg(0), 0);
1295                }
1296                if results == 1 && res(0).is_void() {
1297                    self.error("va_arg reads a value and void is not one");
1298                }
1299            }
1300            Opcode::VaObject => {
1301                if self.takes(opcode, arity, 1) {
1302                    self.pointer(opcode, arg(0), 0);
1303                }
1304                if results == 1 && res(0) != Type::PTR {
1305                    self.error(format!(
1306                        "va_object answers where the object is and {} is not an address",
1307                        res(0)
1308                    ));
1309                }
1310            }
1311
1312            // Control.
1313            Opcode::Jump => {
1314                self.takes(opcode, arity, 0);
1315                self.targets(func, inst, 1);
1316            }
1317            Opcode::BrIf => {
1318                if self.takes(opcode, arity, 1) && arg(0) != Type::I1 {
1319                    self.error(format!("br_if branches on an i1 and this one on {}", arg(0)));
1320                }
1321                self.targets(func, inst, 2);
1322            }
1323            Opcode::Switch => {
1324                if self.takes(opcode, arity, 1) {
1325                    self.integer(opcode, arg(0), 0);
1326                }
1327            }
1328            Opcode::BlockAddr => {
1329                self.takes(opcode, arity, 0);
1330                self.targets(func, inst, 1);
1331                if results == 1 && !res(0).is_ptr() {
1332                    self.error(format!(
1333                        "block_addr produces a pointer and this one produces {}",
1334                        res(0)
1335                    ));
1336                }
1337                if func.successors(inst).any(|call| !func[call.args].is_empty()) {
1338                    // Taking the address is not arriving, so there is nothing to hand over.
1339                    // What the block takes is passed by the branch that goes there.
1340                    self.error("block_addr names a block and passes it arguments");
1341                }
1342            }
1343            Opcode::IndirectBr => {
1344                if self.takes(opcode, arity, 1) {
1345                    self.pointer(opcode, arg(0), 0);
1346                }
1347                // No count to check: how many blocks one of these can arrive at is how many
1348                // the front end says, and a `goto *p` in a function with one label has one.
1349            }
1350            Opcode::Return => {
1351                let want = &func.signature().returns;
1352                if arity != want.len() {
1353                    self.error(format!(
1354                        "the signature returns {} and this returns {arity}",
1355                        want.len()
1356                    ));
1357                } else {
1358                    for (index, ty) in want.iter().map(|param| param.ty).enumerate() {
1359                        if arg(index) != ty {
1360                            self.error(format!(
1361                                "result {} of the signature is {ty} and this returns {}",
1362                                index + 1,
1363                                arg(index)
1364                            ));
1365                        }
1366                    }
1367                }
1368            }
1369
1370            // Calls, whose signature is what says the shape.
1371            Opcode::Call | Opcode::CallIndirect | Opcode::TailCall => {
1372                let Extra::Call(at) = data.extra else { return };
1373                let info = func[at];
1374                let signature = &func[info.signature];
1375                let indirect = usize::from(opcode == Opcode::CallIndirect);
1376                if indirect == 1 {
1377                    if arity == 0 {
1378                        self.error("call_indirect calls through a pointer and has no operands");
1379                        return;
1380                    }
1381                    self.pointer(opcode, arg(0), 0);
1382                }
1383                let passed = arity - indirect;
1384                let enough = if signature.variadic {
1385                    passed >= signature.params.len()
1386                } else {
1387                    passed == signature.params.len()
1388                };
1389                if enough {
1390                    for (index, ty) in signature.param_types().enumerate() {
1391                        if arg(index + indirect) != ty {
1392                            self.error(format!(
1393                                "parameter {} of the signature is {ty} and this argument is {}",
1394                                index + 1,
1395                                arg(index + indirect)
1396                            ));
1397                        }
1398                    }
1399                } else {
1400                    self.error(format!(
1401                        "the signature takes {}{} and this call passes {passed}",
1402                        signature.params.len(),
1403                        if signature.variadic { " or more" } else { "" }
1404                    ));
1405                }
1406                self.varargs(func, &info, passed, |n| arg(n + indirect));
1407                if results != signature.returns.len() {
1408                    self.error(format!(
1409                        "the signature returns {} and this call produces {results}",
1410                        signature.returns.len()
1411                    ));
1412                } else {
1413                    for (index, ty) in signature.return_types().enumerate() {
1414                        if res(index) != ty {
1415                            self.error(format!(
1416                                "result {} of the signature is {ty} and this call produces {}",
1417                                index + 1,
1418                                res(index)
1419                            ));
1420                        }
1421                    }
1422                }
1423                // Where the callee is in this module, the signature at the call and the one at
1424                // the function are the same signature or one of them is wrong.
1425                if let Some(callee) = info.callee {
1426                    if let Some(SymbolRef::Func(id)) = self.module.lookup(callee) {
1427                        if self.module[id].signature() != signature {
1428                            self.error(format!(
1429                                "@{} is declared here with another signature",
1430                                self.names.resolve(callee)
1431                            ));
1432                        }
1433                    }
1434                }
1435            }
1436
1437            // Bit counting, which answers in the type it was asked about.
1438            Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop | Opcode::Bswap | Opcode::Bitreverse => {
1439                if self.takes(opcode, arity, 1) {
1440                    self.integer(opcode, arg(0), 0);
1441                    if results == 1 {
1442                        self.produces(opcode, res(0), arg(0));
1443                    }
1444                }
1445            }
1446
1447            // The overflow-checked forms, which answer the value and whether it wrapped.
1448            Opcode::SAddOverflow
1449            | Opcode::UAddOverflow
1450            | Opcode::SSubOverflow
1451            | Opcode::USubOverflow
1452            | Opcode::SMulOverflow
1453            | Opcode::UMulOverflow => {
1454                if self.takes(opcode, arity, 2) {
1455                    self.integer(opcode, arg(0), 0);
1456                    self.agree(opcode, arg(0), arg(1));
1457                    if results == 2 {
1458                        self.produces(opcode, res(0), arg(0));
1459                        self.produces(opcode, res(1), arg(0).with_lane(Type::I1));
1460                    }
1461                }
1462            }
1463            Opcode::Expect => {
1464                if self.takes(opcode, arity, 2) {
1465                    self.agree(opcode, arg(0), arg(1));
1466                    if results == 1 {
1467                        self.produces(opcode, res(0), arg(0));
1468                    }
1469                }
1470            }
1471
1472            // What is left says nothing about its operands here: the two markers are placed by
1473            // the front end around code the optimizer must not move, inline assembly is
1474            // whatever its constraints say, and a target intrinsic is the target's own rule.
1475            Opcode::SetjmpMarker
1476            | Opcode::LongjmpMarker
1477            | Opcode::InlineAsm
1478            | Opcode::TargetIntrinsic => {}
1479
1480            // Capabilities. The rule that makes these worth checking is that a capability and
1481            // the pointer it describes are two operands and never one, so an instruction that
1482            // took the same value twice, or took them the other way round, is caught here rather
1483            // than becoming a check that passes because it compared a pointer with itself.
1484            Opcode::CapOf | Opcode::CapRecover | Opcode::CapLoad => {
1485                if self.takes(opcode, arity, 1) {
1486                    self.pointer(opcode, arg(0), 0);
1487                    if results == 1 {
1488                        self.produces(opcode, res(0), Type::CAP);
1489                    }
1490                }
1491            }
1492            Opcode::CapNull => {
1493                if self.takes(opcode, arity, 0) && results == 1 {
1494                    self.produces(opcode, res(0), Type::CAP);
1495                }
1496            }
1497            Opcode::CapStore => {
1498                if self.takes(opcode, arity, 2) {
1499                    self.pointer(opcode, arg(0), 0);
1500                    self.capability(opcode, arg(1), 1);
1501                }
1502            }
1503            Opcode::CapExtent | Opcode::CapExtentBack => {
1504                // The capability and the address it is about, as everywhere else here, and then
1505                // how many bytes the asker wants. The answer is a count of the same bytes the
1506                // limit counts, so the two have one type and the caller picks it. The backward
1507                // form counts the bytes below the address rather than the bytes from it on, which
1508                // is a difference in what the answer means and not in the shape of the question.
1509                if self.takes(opcode, arity, 3) {
1510                    self.capability(opcode, arg(0), 0);
1511                    self.pointer(opcode, arg(1), 1);
1512                    self.integer(opcode, arg(2), 2);
1513                    if results == 1 {
1514                        self.produces(opcode, res(0), arg(2));
1515                    }
1516                }
1517            }
1518            Opcode::CapNarrow => {
1519                if self.takes(opcode, arity, 3) {
1520                    self.capability(opcode, arg(0), 0);
1521                    self.integer(opcode, arg(1), 1);
1522                    self.integer(opcode, arg(2), 2);
1523                    self.agree(opcode, arg(1), arg(2));
1524                    if results == 1 {
1525                        self.produces(opcode, res(0), Type::CAP);
1526                    }
1527                }
1528            }
1529
1530            // The checks. Five of them ask about one pointer and the sixth asks whether a
1531            // second one stayed inside the first one's capability, so that is the only
1532            // difference in shape between them.
1533            Opcode::CheckLive | Opcode::CheckType | Opcode::CheckInit | Opcode::CheckRace => {
1534                if self.takes(opcode, arity, 2) {
1535                    self.capability(opcode, arg(0), 0);
1536                    self.pointer(opcode, arg(1), 1);
1537                }
1538            }
1539            Opcode::CheckBounds => {
1540                // Two operands is the access the front end wrote, whose length is in the payload.
1541                // Three is the hoisted check of section 7.4, whose length the program computes, and
1542                // the extra operand is that length in bytes.
1543                if self.takes_either(opcode, arity, 2, 3) {
1544                    self.capability(opcode, arg(0), 0);
1545                    self.pointer(opcode, arg(1), 1);
1546                    if arity == 3 {
1547                        self.integer(opcode, arg(2), 2);
1548                    }
1549                }
1550            }
1551            Opcode::CheckDeriv => {
1552                // The fourth operand is the stride, which is how wide one element of whatever is
1553                // being stepped over is. Document 03 section 3.1 puts S5's window at
1554                // `[lo - stride, hi]`, so the judgement cannot be made without it, and it is an
1555                // operand rather than something written on the instruction because a walk over a
1556                // variable length array steps by a width the program computes.
1557                if self.takes(opcode, arity, 4) {
1558                    self.capability(opcode, arg(0), 0);
1559                    self.pointer(opcode, arg(1), 1);
1560                    self.pointer(opcode, arg(2), 2);
1561                    self.integer(opcode, arg(3), 3);
1562                }
1563            }
1564
1565            // The two `restrict` checks, which are one pointer and nothing else. No capability,
1566            // because what they read is the record the enclosing block keeps rather than any
1567            // plane, and the record is found from the clique in the payload.
1568            Opcode::CheckRestrictRead | Opcode::CheckRestrictWrite => {
1569                if self.takes(opcode, arity, 1) {
1570                    self.pointer(opcode, arg(0), 0);
1571                }
1572                self.names_a_scope(func, inst, opcode);
1573            }
1574
1575            // The markers around a block that declares `restrict` pointers. The operand is the
1576            // storage the record lives in, so it is a pointer, and it is the same one both times.
1577            Opcode::RestrictEnter => {
1578                if self.takes(opcode, arity, 1) {
1579                    self.pointer(opcode, arg(0), 0);
1580                }
1581                self.names_a_scope(func, inst, opcode);
1582            }
1583            Opcode::RestrictLeave => {
1584                if self.takes(opcode, arity, 1) {
1585                    self.pointer(opcode, arg(0), 0);
1586                }
1587            }
1588
1589            // The plane writes, which are all a range: where it starts and how long it is. No
1590            // capability, because a plane write is what makes a capability true rather than
1591            // something checked against one.
1592            Opcode::MetaBegin
1593            | Opcode::MetaEnd
1594            | Opcode::MetaType
1595            | Opcode::MetaInit
1596            | Opcode::MetaTransfer => {
1597                if self.takes(opcode, arity, 2) {
1598                    self.pointer(opcode, arg(0), 0);
1599                    self.integer(opcode, arg(1), 1);
1600                }
1601            }
1602
1603            // The two plane writes that are about two ranges rather than one. Neither carries a
1604            // node, because what the destination ends up saying is whatever the source said and the
1605            // compiler does not know that: the plane over the source is where it is written down.
1606            Opcode::MetaTypeCopy | Opcode::MetaInitCopy => {
1607                if self.takes(opcode, arity, 3) {
1608                    self.pointer(opcode, arg(0), 0);
1609                    self.pointer(opcode, arg(1), 1);
1610                    self.integer(opcode, arg(2), 2);
1611                }
1612            }
1613
1614            // The region markers, which say something about the code between them rather than
1615            // about any value, so there is nothing here but the operand count.
1616            Opcode::SafeRegionBegin | Opcode::SafeRegionEnd => {
1617                self.takes(opcode, arity, 0);
1618            }
1619        }
1620    }
1621
1622    // The small questions the shapes are made of.
1623
1624    /// Whether the payload of a `restrict` instruction names a scope for the reader to find.
1625    ///
1626    /// Clique zero is how an access says the names did not tell the front end where its pointer
1627    /// came from, and such an access carries no check at all. One that does carry a check and says
1628    /// nothing about which scope it is in would be passed by the runtime every time, so it is a
1629    /// lowering bug wearing the shape of a check rather than a check.
1630    fn names_a_scope(&mut self, func: &'a Func, inst: Inst, opcode: Opcode) {
1631        let Extra::Mem(at) = func[inst].extra else { return };
1632        if func[at].restrict.clique == 0 {
1633            self.error(format!(
1634                "{} is in no restrict scope, so it would ask nothing",
1635                opcode.name()
1636            ));
1637        }
1638    }
1639
1640    /// Reports the operand count when it is wrong, and answers whether it was right.
1641    fn takes(&mut self, opcode: Opcode, got: usize, want: usize) -> bool {
1642        if got == want {
1643            return true;
1644        }
1645        self.error(format!("{} takes {want} operands and this one has {got}", opcode.name()));
1646        false
1647    }
1648
1649    /// Reports the number of operands when it is neither of the two an opcode allows.
1650    fn takes_either(&mut self, opcode: Opcode, got: usize, one: usize, other: usize) -> bool {
1651        if got == one || got == other {
1652            return true;
1653        }
1654        self.error(format!(
1655            "{} takes {one} or {other} operands and this one has {got}",
1656            opcode.name()
1657        ));
1658        false
1659    }
1660
1661    /// Reports the number of branch targets when it is wrong.
1662    fn targets(&mut self, func: &'a Func, inst: Inst, want: usize) {
1663        let got = func.successors(inst).count();
1664        if got != want {
1665            self.error(format!(
1666                "{} branches to {want} blocks and this one to {got}",
1667                func[inst].opcode.name()
1668            ));
1669        }
1670    }
1671
1672    fn integer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1673        if !ty.lane().is_int() {
1674            self.error(format!(
1675                "operand {} of {} is an integer and this one is {ty}",
1676                n + 1,
1677                opcode.name()
1678            ));
1679        }
1680    }
1681
1682    fn floating(&mut self, opcode: Opcode, ty: Type, n: usize) {
1683        if !ty.lane().is_float() {
1684            self.error(format!(
1685                "operand {} of {} is a floating point value and this one is {ty}",
1686                n + 1,
1687                opcode.name()
1688            ));
1689        }
1690    }
1691
1692    fn capability(&mut self, opcode: Opcode, ty: Type, n: usize) {
1693        if !ty.is_cap() {
1694            self.error(format!(
1695                "operand {} of {} is a capability and this one is {ty}",
1696                n + 1,
1697                opcode.name()
1698            ));
1699        }
1700    }
1701
1702    fn pointer(&mut self, opcode: Opcode, ty: Type, n: usize) {
1703        if !ty.is_ptr() {
1704            self.error(format!(
1705                "operand {} of {} is a pointer and this one is {ty}",
1706                n + 1,
1707                opcode.name()
1708            ));
1709        }
1710    }
1711
1712    /// Two operands that have to be the same type as each other.
1713    fn agree(&mut self, opcode: Opcode, first: Type, second: Type) {
1714        if first != second {
1715            self.error(format!(
1716                "the operands of {} have one type and these are {first} and {second}",
1717                opcode.name()
1718            ));
1719        }
1720    }
1721
1722    /// A result that has to be a particular type.
1723    fn produces(&mut self, opcode: Opcode, got: Type, want: Type) {
1724        if got != want {
1725            self.error(format!(
1726                "{} produces {want} here and this one produces {got}",
1727                opcode.name()
1728            ));
1729        }
1730    }
1731
1732    /// A conversion that keeps the lane count, since none of them changes it.
1733    fn lanes(&mut self, opcode: Opcode, to: Type, from: Type) {
1734        if to.lanes() != from.lanes() {
1735            self.error(format!(
1736                "{} keeps the lane count and {from} has {} and {to} has {}",
1737                opcode.name(),
1738                from.lanes(),
1739                to.lanes()
1740            ));
1741        }
1742    }
1743
1744    /// A conversion that has to go the way its name says.
1745    fn widens(&mut self, opcode: Opcode, to: Type, from: Type, wider: bool) {
1746        let (a, b) = (to.lane().bits(), from.lane().bits());
1747        let ok = if wider { a > b } else { a < b };
1748        if !ok {
1749            let way = if wider { "wider" } else { "narrower" };
1750            self.error(format!(
1751                "{} produces something {way} and {from} to {to} is not",
1752                opcode.name()
1753            ));
1754        }
1755    }
1756
1757    // Reporting.
1758
1759    fn error(&mut self, message: impl Into<String>) {
1760        let at = self.locate();
1761        self.at(at, message);
1762    }
1763
1764    fn at(&mut self, at: String, message: impl Into<String>) {
1765        self.errors.push(VerifyError { at, message: message.into() });
1766    }
1767
1768    /// Where the walk is, as text.
1769    ///
1770    /// A block is named by its index rather than by the number the printer would give it,
1771    /// which is the same number for a module that came from the parser and can differ for one
1772    /// a pass has been reordering.
1773    fn locate(&self) -> String {
1774        use fmt::Write as _;
1775        let mut at = String::new();
1776        if let Some(func) = self.func {
1777            let _ = write!(at, "@{}", self.names.resolve(func.name));
1778            if let Some(block) = self.block {
1779                let _ = write!(at, " block{}", block.raw());
1780            }
1781            if let Some(inst) = self.inst {
1782                let _ = write!(at, " {}", func[inst].opcode.name());
1783            }
1784        }
1785        at
1786    }
1787}
1788
1789/// Where each instruction is, so that a use in the same block as its definition can be told
1790/// from a use before it.
1791struct Layout {
1792    block: Vec<Option<Block>>,
1793    position: Vec<u32>,
1794}
1795
1796impl Layout {
1797    fn new(func: &Func) -> Self {
1798        let counts = func.counts();
1799        let mut layout =
1800            Layout { block: vec![None; counts.insts], position: vec![0; counts.insts] };
1801        for block in func.blocks() {
1802            for (position, inst) in func.insts(block).enumerate() {
1803                layout.block[inst.index()] = Some(block);
1804                layout.position[inst.index()] = position as u32;
1805            }
1806        }
1807        layout
1808    }
1809
1810    fn block_of(&self, inst: Inst) -> Option<Block> {
1811        self.block[inst.index()]
1812    }
1813
1814    fn position(&self, inst: Inst) -> u32 {
1815        self.position[inst.index()]
1816    }
1817}
1818
1819/// Which block dominates which, by the iterative algorithm of Cooper, Harvey and Kennedy,
1820/// "A Simple, Fast Dominance Algorithm" (2001).
1821///
1822/// The one in the verifier rather than a shared analysis, because the optimizer's dominator
1823/// tree is incrementally maintained across a pass and this one is built from nothing every time
1824/// it is asked for. The two want different things from the same idea.
1825struct Doms {
1826    /// Where each block is in reverse postorder, which is the order the fixed point converges
1827    /// fastest in, and `None` for one the entry does not reach.
1828    rank: Vec<Option<u32>>,
1829    /// The rank of each block's immediate dominator, indexed by rank.
1830    idom: Vec<u32>,
1831}
1832
1833impl Doms {
1834    fn new(func: &Func) -> Self {
1835        let counts = func.counts();
1836        let entry = func.entry().expect("a function with blocks has a first one");
1837
1838        // Every instruction that names a block, and not only the terminator. The one other
1839        // instruction that names one is `block_addr`, whose block is somewhere an
1840        // `indirect_br` can arrive at from anywhere the address reaches, so counting it as an
1841        // edge is what keeps a label that is only jumped to indirectly out of the reachability
1842        // report. The edge is a real one in the only direction that matters here: it can add
1843        // predecessors to a block and so take dominators away from it, which makes the check
1844        // on the uses stricter and never looser.
1845        let mut succs: Vec<Vec<Block>> = vec![Vec::new(); counts.blocks];
1846        for block in func.blocks() {
1847            for inst in func.insts(block) {
1848                succs[block.index()].extend(func.successors(inst).map(|call| call.block));
1849            }
1850        }
1851
1852        // Postorder by an explicit stack, since a chain of blocks can be as long as the
1853        // function is and the recursive form would run out of stack on one.
1854        let mut order = Vec::new();
1855        let mut seen = vec![false; counts.blocks];
1856        let mut stack = vec![(entry, 0usize)];
1857        seen[entry.index()] = true;
1858        while let Some((block, next)) = stack.pop() {
1859            match succs[block.index()].get(next) {
1860                Some(&target) => {
1861                    stack.push((block, next + 1));
1862                    if !seen[target.index()] {
1863                        seen[target.index()] = true;
1864                        stack.push((target, 0));
1865                    }
1866                }
1867                None => order.push(block),
1868            }
1869        }
1870        order.reverse();
1871
1872        let mut rank = vec![None; counts.blocks];
1873        for (index, &block) in order.iter().enumerate() {
1874            rank[block.index()] = Some(index as u32);
1875        }
1876        let mut preds: Vec<Vec<u32>> = vec![Vec::new(); order.len()];
1877        for (index, &block) in order.iter().enumerate() {
1878            for &target in &succs[block.index()] {
1879                if let Some(target) = rank[target.index()] {
1880                    preds[target as usize].push(index as u32);
1881                }
1882            }
1883        }
1884
1885        // The entry dominates itself, and everything else starts undefined, which is what the
1886        // sentinel is. The fixed point is reached in one pass over a reducible CFG and in two
1887        // over the ones a `goto` produces.
1888        const NONE: u32 = u32::MAX;
1889        let mut idom = vec![NONE; order.len()];
1890        if !order.is_empty() {
1891            idom[0] = 0;
1892        }
1893        let mut changed = true;
1894        while changed {
1895            changed = false;
1896            for index in 1..order.len() {
1897                let mut new = NONE;
1898                for &pred in &preds[index] {
1899                    if idom[pred as usize] == NONE {
1900                        continue;
1901                    }
1902                    new = if new == NONE { pred } else { meet(&idom, new, pred) };
1903                }
1904                if new != NONE && idom[index] != new {
1905                    idom[index] = new;
1906                    changed = true;
1907                }
1908            }
1909        }
1910        Doms { rank, idom }
1911    }
1912
1913    /// Whether the entry block reaches this one at all.
1914    fn reaches(&self, block: Block) -> bool {
1915        self.rank[block.index()].is_some()
1916    }
1917
1918    /// Whether every path from the entry to `block` goes through `of`.
1919    ///
1920    /// A block the entry does not reach is dominated by everything, which is vacuously true and
1921    /// keeps an unreachable block from being reported twice over, once for being unreachable
1922    /// and once for every value it uses.
1923    fn dominates(&self, of: Block, block: Block) -> bool {
1924        let (Some(a), Some(b)) = (self.rank[of.index()], self.rank[block.index()]) else {
1925            return true;
1926        };
1927        let mut walk = b;
1928        while walk > a {
1929            walk = self.idom[walk as usize];
1930        }
1931        walk == a
1932    }
1933}
1934
1935/// Whether the new version of memory is one result on top of what the opcode produces anyway.
1936///
1937/// It is, for every instruction that writes memory, and it is not for `mem_entry`, whose one
1938/// result is the version of memory the function starts with.
1939fn extra_mem(func: &Func, inst: Inst) -> bool {
1940    func[inst].opcode.writes_memory() && func.mem_out(inst).is_some()
1941}
1942
1943/// The nearest block that dominates both, walking the two chains towards the entry.
1944fn meet(idom: &[u32], mut a: u32, mut b: u32) -> u32 {
1945    while a != b {
1946        while a > b {
1947            a = idom[a as usize];
1948        }
1949        while b > a {
1950            b = idom[b as usize];
1951        }
1952    }
1953    a
1954}
1955
1956/// How many bits a value of that type occupies, counting every lane.
1957fn width(ty: Type) -> u64 {
1958    u64::from(ty.bits()) * u64::from(ty.lanes())
1959}
1960
1961/// A list of types, as the text writes them.
1962fn types(list: &[Type]) -> String {
1963    if list.is_empty() {
1964        return "nothing".to_string();
1965    }
1966    list.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
1967}
1968
1969#[cfg(test)]
1970mod tests {
1971    use rucc_base::{Idx, Interner, Symbol};
1972    use rucc_diag::Span;
1973    use rucc_target::{Arch, Env, Os, Triple};
1974
1975    use super::*;
1976    use crate::fixtures::{EXAMPLE, SAFETY, SYMBOLS, ZOO};
1977    use crate::func::Builder;
1978    use crate::inst::{InstData, MetaNode, Signature, TbaaNode};
1979    use crate::{Flags, IntPred, parse};
1980
1981    fn target() -> TargetInfo {
1982        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1983    }
1984
1985    /// Everything wrong with a module written as text, which is how most of these are written:
1986    /// the parser turns down what is malformed and what is left is what the verifier is for.
1987    fn errors(text: &str) -> Vec<String> {
1988        let mut names = Interner::new();
1989        let module = match parse(text, &mut names) {
1990            Ok(module) => module,
1991            Err(error) => panic!("{error}"),
1992        };
1993        match verify(&module, &names) {
1994            Ok(()) => Vec::new(),
1995            Err(errors) => errors.iter().map(ToString::to_string).collect(),
1996        }
1997    }
1998
1999    /// The one thing wrong with it.
2000    fn only(text: &str) -> String {
2001        let found = errors(text);
2002        assert_eq!(found.len(), 1, "{found:#?}");
2003        found.into_iter().next().expect("just counted one")
2004    }
2005
2006    const HEADER: &str = "\
2007; ModuleID = 'bad.c'
2008; format 0
2009target triple = \"x86_64-unknown-linux-gnu\"
2010target datalayout = \"e-p:64:64-i64:64-f80:128-S128\"
2011";
2012
2013    /// A function around that body, with that signature.
2014    fn wrap(signature: &str, body: &str) -> String {
2015        format!("{HEADER}\nfunc @f{signature}, linkage(external) {{\n{body}}}\n")
2016    }
2017
2018    /// Whether any of the reports says that.
2019    ///
2020    /// The capability rules below fire more than once on purpose. A parameter of that type is
2021    /// both a signature the ABI has no answer for and a value nothing produced, and reporting one
2022    /// of the two and not the other would mean the second went unnoticed the day the first was
2023    /// relaxed.
2024    fn reports(text: &str, message: &str) {
2025        let found = errors(text);
2026        assert!(found.iter().any(|error| error.contains(message)), "{message}\nin {found:#?}");
2027    }
2028
2029    #[test]
2030    fn the_four_fixtures_are_modules_the_compiler_may_believe() {
2031        for text in [EXAMPLE, ZOO, SYMBOLS, SAFETY] {
2032            assert_eq!(errors(text), Vec::<String>::new());
2033        }
2034    }
2035
2036    /// A function on the memory chain, which every rule below breaks one way or another.
2037    const CHAIN: &str = "block0(%0: ptr):
2038    %1 = mem_entry
2039    %2 = iconst.i32 7
2040    %3 = store %2 -> %0, size 4, align 4 [mem %1]
2041    %4 = load.i32 %0, size 4, align 4 [mem %3]
2042    return %4
2043";
2044
2045    #[test]
2046    fn a_function_carrying_memory_is_one_the_compiler_may_believe() {
2047        assert_eq!(errors(&wrap("(ptr) -> i32", CHAIN)), Vec::<String>::new());
2048    }
2049
2050    #[test]
2051    fn an_instruction_that_touches_no_memory_and_is_on_the_chain_is_reported() {
2052        let text = wrap(
2053            "(ptr) -> i32",
2054            "block0(%0: ptr):
2055    %1 = mem_entry
2056    %2 = iconst.i32 7
2057    %3 = store %2 -> %0, size 4, align 4 [mem %1]
2058    %4 = add %2, %2 [mem %3]
2059    return %4
2060",
2061        );
2062        assert_eq!(
2063            only(&text),
2064            "@f block0 add: add does not touch memory and is on the memory chain"
2065        );
2066    }
2067
2068    #[test]
2069    fn a_write_on_the_chain_at_one_end_only_is_reported() {
2070        let text = wrap(
2071            "(ptr) -> i32",
2072            "block0(%0: ptr):
2073    %1 = mem_entry
2074    %2 = iconst.i32 7
2075    store %2 -> %0, size 4, align 4 [mem %1]
2076    %3 = load.i32 %0, size 4, align 4 [mem %1]
2077    return %3
2078",
2079        );
2080        assert_eq!(
2081            only(&text),
2082            "@f block0 store: store takes memory and produces none, and a write does both or neither"
2083        );
2084    }
2085
2086    #[test]
2087    fn memory_reaching_somewhere_that_is_not_the_memory_operand_is_reported() {
2088        let text = wrap(
2089            "(ptr) -> i32",
2090            "block0(%0: ptr):
2091    %1 = mem_entry
2092    %2 = iconst.i32 7
2093    %3 = store %2 -> %0, size 4, align 4 [mem %1]
2094    %4 = store %1 -> %0, size 8, align 8 [mem %3]
2095    %5 = load.i32 %0, size 4, align 4 [mem %4]
2096    return %5
2097",
2098        );
2099        assert_eq!(
2100            only(&text),
2101            "@f block0 store: %1 is memory and operand 1 of store is not the memory operand"
2102        );
2103    }
2104
2105    #[test]
2106    fn a_memory_phi_is_an_ordinary_block_parameter() {
2107        let text = wrap(
2108            "(ptr, i1) -> i32",
2109            "block0(%0: ptr, %1: i1):
2110    %2 = mem_entry
2111    br_if %1, block1(%2), block2(%2)
2112
2113block1(%3: mem):
2114    %4 = iconst.i32 7
2115    %5 = store %4 -> %0, size 4, align 4 [mem %3]
2116    jump block3(%5)
2117
2118block2(%6: mem):
2119    jump block3(%6)
2120
2121block3(%7: mem):
2122    %8 = load.i32 %0, size 4, align 4 [mem %7]
2123    return %8
2124",
2125        );
2126        assert_eq!(errors(&text), Vec::<String>::new());
2127    }
2128
2129    #[test]
2130    fn a_second_start_to_the_chain_is_reported() {
2131        let text = wrap(
2132            "(ptr) -> i32",
2133            "block0(%0: ptr):
2134    %1 = mem_entry
2135    %2 = mem_entry
2136    %3 = iconst.i32 7
2137    %4 = store %3 -> %0, size 4, align 4 [mem %1]
2138    %5 = load.i32 %0, size 4, align 4 [mem %4]
2139    return %5
2140",
2141        );
2142        assert_eq!(
2143            only(&text),
2144            "@f block0 mem_entry: a function starts with one version of memory and this is a second one"
2145        );
2146    }
2147
2148    #[test]
2149    fn a_chain_with_no_start_is_reported() {
2150        // The only way to reach this is a function that says it takes memory, which is the other
2151        // thing wrong with it, because the chain starts at `mem_entry` and nowhere else.
2152        let text = wrap(
2153            "(ptr, mem) -> i32",
2154            "block0(%0: ptr, %1: mem):
2155    %2 = iconst.i32 7
2156    %3 = store %2 -> %0, size 4, align 4 [mem %1]
2157    %4 = load.i32 %0, size 4, align 4 [mem %3]
2158    return %4
2159",
2160        );
2161        assert_eq!(
2162            errors(&text),
2163            [
2164                "@f: parameter 2 is memory, and no function takes or returns memory",
2165                "@f block0 store: this is on the memory chain and the function has no mem_entry",
2166            ]
2167        );
2168    }
2169
2170    #[test]
2171    fn half_a_function_on_the_chain_is_reported() {
2172        let text = wrap(
2173            "(ptr) -> i32",
2174            "block0(%0: ptr):
2175    %1 = mem_entry
2176    %2 = iconst.i32 7
2177    %3 = store %2 -> %0, size 4, align 4 [mem %1]
2178    %4 = load.i32 %0, size 4, align 4
2179    return %4
2180",
2181        );
2182        assert_eq!(
2183            only(&text),
2184            "@f block0 load: load touches memory and is not on the chain, and the rest of this function is"
2185        );
2186    }
2187
2188    #[test]
2189    fn a_chain_that_starts_and_goes_nowhere_is_reported() {
2190        let text = wrap(
2191            "(ptr) -> i32",
2192            "block0(%0: ptr):
2193    %1 = mem_entry
2194    %2 = load.i32 %0, size 4, align 4
2195    return %2
2196",
2197        );
2198        assert_eq!(
2199            only(&text),
2200            "@f block0 mem_entry: nothing in this function is on the memory chain, and this starts one"
2201        );
2202    }
2203
2204    #[test]
2205    fn a_start_to_the_chain_outside_the_entry_block_is_reported() {
2206        let text = wrap(
2207            "(ptr, i1) -> i32",
2208            "block0(%0: ptr, %1: i1):
2209    br_if %1, block1, block2
2210
2211block1:
2212    %2 = mem_entry
2213    %3 = load.i32 %0, size 4, align 4 [mem %2]
2214    return %3
2215
2216block2:
2217    %4 = iconst.i32 0
2218    return %4
2219",
2220        );
2221        assert_eq!(only(&text), "@f block1 mem_entry: mem_entry belongs in the entry block");
2222    }
2223
2224    #[test]
2225    fn a_use_that_its_definition_does_not_reach_is_reported() {
2226        let text = wrap(
2227            "(i1) -> i32",
2228            "block0(%0: i1):
2229    br_if %0, block1, block2
2230
2231block1:
2232    %1 = iconst.i32 7
2233    jump block2
2234
2235block2:
2236    return %1
2237",
2238        );
2239        assert_eq!(
2240            only(&text),
2241            "@f block2 return: %1 is produced in block1 and does not reach here"
2242        );
2243    }
2244
2245    #[test]
2246    fn a_use_before_its_definition_in_the_same_block_is_reported() {
2247        let text = wrap(
2248            "() -> i32",
2249            "block0:
2250    %0 = iconst.i32 1
2251    %1 = add %2, %0
2252    %2 = iconst.i32 2
2253    return %1
2254",
2255        );
2256        assert_eq!(only(&text), "@f block0 add: %2 is produced in block0 and does not reach here");
2257    }
2258
2259    #[test]
2260    fn a_call_that_takes_its_arguments_the_way_the_abi_says_is_believed() {
2261        let text = wrap(
2262            "(ptr sret(24, align 8), ptr byval(16, align 8), i8 zext)",
2263            "block0(%0: ptr, %1: ptr, %2: i8):
2264    return
2265",
2266        );
2267        assert_eq!(errors(&text), Vec::<String>::new());
2268    }
2269
2270    #[test]
2271    fn a_call_that_says_how_an_argument_past_its_parameter_list_travels_is_believed() {
2272        let text = wrap(
2273            "(ptr)",
2274            "block0(%0: ptr):
2275    call @p(%0, %0 byval(24, align 8)) : (ptr, ...)
2276    return
2277",
2278        );
2279        assert_eq!(errors(&text), Vec::<String>::new());
2280    }
2281
2282    #[test]
2283    fn a_call_that_says_it_of_something_that_is_not_a_pointer_is_reported() {
2284        let text = wrap(
2285            "(ptr)",
2286            "block0(%0: ptr):
2287    %1 = iconst.i32 1
2288    call @p(%0, %1 byval(4, align 4)) : (ptr, ...)
2289    return
2290",
2291        );
2292        assert_eq!(
2293            only(&text),
2294            "@f block0 call: argument 2 travels indirectly and i32 is not a pointer"
2295        );
2296    }
2297
2298    #[test]
2299    fn a_call_that_says_an_argument_past_its_parameter_list_is_an_sret_is_reported() {
2300        let text = wrap(
2301            "(ptr)",
2302            "block0(%0: ptr):
2303    call @p(%0, %0 sret(24, align 8)) : (ptr, ...)
2304    return
2305",
2306        );
2307        assert_eq!(
2308            only(&text),
2309            "@f block0 call: argument 2 is an sret and only a parameter can be one"
2310        );
2311    }
2312
2313    #[test]
2314    fn a_call_that_is_not_variadic_says_nothing_about_a_variadic_argument() {
2315        let text = wrap(
2316            "(ptr)",
2317            "block0(%0: ptr):
2318    call @p(%0, %0 byval(24, align 8)) : (ptr)
2319    return
2320",
2321        );
2322        assert_eq!(
2323            errors(&text),
2324            vec![
2325                "@f block0 call: the signature takes 1 and this call passes 2",
2326                "@f block0 call: this call is not variadic and says how a variadic argument \
2327                 travels",
2328            ]
2329        );
2330    }
2331
2332    #[test]
2333    fn an_sret_that_is_not_the_first_parameter_is_reported() {
2334        let text = wrap(
2335            "(ptr byval(8, align 8), ptr sret(16, align 8))",
2336            "block0(%0: ptr, %1: ptr):
2337    return
2338",
2339        );
2340        assert_eq!(only(&text), "@f: sret is the first parameter and this one is not");
2341    }
2342
2343    #[test]
2344    fn a_function_returning_through_sret_returns_nothing_else() {
2345        let text = wrap(
2346            "(ptr sret(8, align 8)) -> i32",
2347            "block0(%0: ptr):
2348    %1 = iconst.i32 7
2349    return %1
2350",
2351        );
2352        assert_eq!(only(&text), "@f: a signature returning through sret returns nothing else");
2353    }
2354
2355    #[test]
2356    fn an_object_that_travels_indirectly_travels_behind_a_pointer() {
2357        let text = wrap(
2358            "(i32 byval(4, align 4))",
2359            "block0(%0: i32):
2360    return
2361",
2362        );
2363        assert_eq!(only(&text), "@f: parameter 1 travels indirectly and i32 is not a pointer");
2364    }
2365
2366    #[test]
2367    fn an_alignment_a_parameter_could_not_have_is_reported() {
2368        let text = wrap(
2369            "(ptr byval(24, align 3))",
2370            "block0(%0: ptr):
2371    return
2372",
2373        );
2374        assert_eq!(only(&text), "@f: an alignment is a power of two and this is 3");
2375    }
2376
2377    /// The slots say which bytes of the object came out of which register, so one reaching past
2378    /// the end of the object is a classification that belongs to some other object, and a backend
2379    /// acting on it would copy memory this object never had.
2380    #[test]
2381    fn a_slot_holding_bytes_the_object_does_not_have_is_reported() {
2382        let text = wrap(
2383            "(ptr) -> ptr",
2384            "block0(%0: ptr):
2385    %1 = va_object %0, size 12, align 8, in(int 8 at 0, int 8 at 8)
2386    return %1
2387",
2388        );
2389        assert_eq!(
2390            only(&text),
2391            "@f block0 va_object: a slot holds bytes 8 to 16 of an object of 12 bytes"
2392        );
2393    }
2394
2395    #[test]
2396    fn a_result_does_not_travel_indirectly() {
2397        // A return value too large for the registers comes back through a parameter, so this
2398        // says an ABI nothing implements.
2399        let text = wrap(
2400            "() -> ptr byval(16, align 8)",
2401            "block0:
2402    %0 = iconst.i64 0
2403    %1 = inttoptr.ptr %0
2404    return %1
2405",
2406        );
2407        assert_eq!(only(&text), "@f: result 1 travels indirectly and a result cannot");
2408    }
2409
2410    #[test]
2411    fn an_extension_is_asked_of_an_integer_and_not_of_anything_else() {
2412        let text = wrap(
2413            "(ptr zext)",
2414            "block0(%0: ptr):
2415    return
2416",
2417        );
2418        assert_eq!(only(&text), "@f: parameter 1 is extended and ptr is not an integer");
2419    }
2420
2421    #[test]
2422    fn a_branch_that_passes_the_wrong_number_of_arguments_is_reported() {
2423        let text = wrap(
2424            "(i32)",
2425            "block0(%0: i32):
2426    jump block1(%0)
2427
2428block1:
2429    return
2430",
2431        );
2432        assert_eq!(
2433            only(&text),
2434            "@f block0 jump: block1 takes 0 arguments and this branch passes 1"
2435        );
2436    }
2437
2438    #[test]
2439    fn a_branch_that_passes_the_wrong_type_is_reported() {
2440        let text = wrap(
2441            "(i32) -> i32",
2442            "block0(%0: i32):
2443    %1 = sext.i64 %0
2444    jump block1(%1)
2445
2446block1(%2: i32):
2447    return %2
2448",
2449        );
2450        assert_eq!(only(&text), "@f block0 jump: argument 1 to block1 is i32 and this one is i64");
2451    }
2452
2453    #[test]
2454    fn a_block_that_does_not_end_in_a_terminator_is_reported() {
2455        let text = wrap("()", "block0:\n    %0 = iconst.i32 1\n");
2456        assert_eq!(only(&text), "@f block0: this block does not end in a terminator");
2457    }
2458
2459    #[test]
2460    fn an_instruction_after_the_terminator_is_reported() {
2461        let text = wrap("()", "block0:\n    return\n    %0 = iconst.i32 1\n");
2462        assert_eq!(only(&text), "@f block0 iconst: this comes after the block's terminator");
2463    }
2464
2465    #[test]
2466    fn an_unreachable_block_is_reported() {
2467        let text = wrap("()", "block0:\n    return\n\nblock1:\n    return\n");
2468        assert_eq!(only(&text), "@f block1: this block is not reachable and has not been deleted");
2469    }
2470
2471    #[test]
2472    fn a_block_a_jump_to_an_address_arrives_at_is_an_ordinary_target() {
2473        let text = wrap(
2474            "(ptr) -> i32",
2475            "block0(%0: ptr):
2476    %1 = block_addr block1
2477    indirect_br %0, block1
2478
2479block1:
2480    %2 = iconst.i32 1
2481    return %2
2482",
2483        );
2484        assert_eq!(errors(&text), Vec::<String>::new());
2485    }
2486
2487    #[test]
2488    fn a_block_whose_address_is_taken_is_reached_by_the_taking_of_it() {
2489        // Nothing branches to block1 here and its address has left the function, so the one
2490        // thing that says it is still somewhere control can arrive at is the `block_addr`.
2491        let text = wrap(
2492            "() -> ptr",
2493            "block0:
2494    %0 = block_addr block1
2495    return %0
2496
2497block1:
2498    unreachable
2499",
2500        );
2501        assert_eq!(errors(&text), Vec::<String>::new());
2502    }
2503
2504    #[test]
2505    fn taking_the_address_of_a_block_and_passing_it_arguments_is_reported() {
2506        // Taking the address is not arriving, so there is nothing to hand over. What block1
2507        // takes is passed by whatever branches there.
2508        let text = wrap(
2509            "(i32) -> ptr",
2510            "block0(%0: i32):
2511    %1 = block_addr block1(%0)
2512    return %1
2513
2514block1(%2: i32):
2515    unreachable
2516",
2517        );
2518        assert_eq!(
2519            only(&text),
2520            "@f block0 block_addr: block_addr names a block and passes it arguments"
2521        );
2522    }
2523
2524    #[test]
2525    fn a_jump_to_something_that_is_not_an_address_is_reported() {
2526        let text = wrap(
2527            "(i32)",
2528            "block0(%0: i32):
2529    indirect_br %0, block1
2530
2531block1:
2532    return
2533",
2534        );
2535        assert_eq!(
2536            only(&text),
2537            "@f block0 indirect_br: operand 1 of indirect_br is a pointer and this one is i32"
2538        );
2539    }
2540
2541    #[test]
2542    fn a_branch_back_to_the_entry_block_is_reported() {
2543        // The entry block's parameters are the function's arguments, so a branch to it would be
2544        // a second place they arrive from.
2545        let text = wrap(
2546            "(i32)",
2547            "block0(%0: i32):
2548    jump block1
2549
2550block1:
2551    jump block0(%0)
2552",
2553        );
2554        assert_eq!(
2555            only(&text),
2556            "@f block0: the entry block is branched to, and it takes the arguments"
2557        );
2558    }
2559
2560    #[test]
2561    fn an_entry_block_that_does_not_take_the_arguments_is_reported() {
2562        let text = wrap("(i32)", "block0(%0: i64):\n    return\n");
2563        assert_eq!(only(&text), "@f: the entry block takes i64 and the signature says i32");
2564    }
2565
2566    #[test]
2567    fn a_flag_the_opcode_does_not_read_is_reported() {
2568        let text =
2569            wrap("(i32) -> i32", "block0(%0: i32):\n    %1 = add.exact %0, %0\n    return %1\n");
2570        assert_eq!(only(&text), "@f block0 add: add does not read `exact`");
2571    }
2572
2573    #[test]
2574    fn an_ordering_the_operation_cannot_be_asked_for_is_reported() {
2575        let text = wrap(
2576            "(ptr) -> i32",
2577            "block0(%0: ptr):\n    %1 = atomic_load.i32 %0, align 4, release\n    return %1\n",
2578        );
2579        assert_eq!(only(&text), "@f block0 atomic_load: atomic_load cannot be asked for release");
2580    }
2581
2582    #[test]
2583    fn an_ordering_on_the_non_atomic_form_is_reported() {
2584        let text = wrap(
2585            "(ptr) -> i32",
2586            "block0(%0: ptr):\n    %1 = load.i32 %0, align 4, acquire\n    return %1\n",
2587        );
2588        assert_eq!(only(&text), "@f block0 load: load cannot be asked for acquire");
2589    }
2590
2591    #[test]
2592    fn a_restrict_base_outside_any_clique_is_reported() {
2593        let text = wrap(
2594            "(ptr) -> i32",
2595            "block0(%0: ptr):\n    %1 = load.i32 %0, align 4, restrict(0, 2)\n    return %1\n",
2596        );
2597        assert_eq!(
2598            only(&text),
2599            "@f block0 load: restrict base 2 is in no clique, and a base means nothing without one"
2600        );
2601    }
2602
2603    #[test]
2604    fn padding_owned_by_something_that_is_not_a_store_is_reported() {
2605        // The number says how far a store records into the init plane, and nothing but a store
2606        // records anything, so one on a read is either a lowering bug or somebody meaning a
2607        // different thing by the word.
2608        let text = wrap(
2609            "(ptr) -> i32",
2610            "block0(%0: ptr):\n    %1 = load.i32 %0, align 4, owns 8\n    return %1\n",
2611        );
2612        assert_eq!(
2613            only(&text),
2614            "@f block0 load: a load owns no padding, since only a store records any"
2615        );
2616    }
2617
2618    #[test]
2619    fn a_va_object_that_answers_anything_but_an_address_is_reported() {
2620        // Where the object is, which is the only thing it can answer, since the object it reads
2621        // is an aggregate and an aggregate is not a value the IR has a type for.
2622        let text = wrap(
2623            "(ptr) -> i64",
2624            "block0(%0: ptr):
2625    %1 = va_object.i64 %0, size 16, align 8
2626    return %1
2627",
2628        );
2629        assert_eq!(
2630            only(&text),
2631            "@f block0 va_object: va_object answers where the object is and i64 is not an address"
2632        );
2633    }
2634
2635    #[test]
2636    fn an_alloca_of_a_fixed_size_outside_the_entry_block_is_reported() {
2637        let text = wrap(
2638            "()",
2639            "block0:
2640    jump block1
2641
2642block1:
2643    %0 = alloca, size 16, align 8
2644    return
2645",
2646        );
2647        assert_eq!(
2648            only(&text),
2649            "@f block1 alloca: an alloca of a fixed size belongs in the entry block"
2650        );
2651    }
2652
2653    #[test]
2654    fn a_dynamic_alloca_may_be_anywhere() {
2655        let text = wrap(
2656            "(i64)",
2657            "block0(%0: i64):
2658    jump block1
2659
2660block1:
2661    %1 = alloca %0, align 8
2662    return
2663",
2664        );
2665        assert_eq!(errors(&text), Vec::<String>::new());
2666    }
2667
2668    #[test]
2669    fn two_cases_of_a_switch_with_the_same_value_are_reported() {
2670        let text = wrap(
2671            "(i32)",
2672            "block0(%0: i32):
2673    switch %0, block1, [7 => block1, 7 => block1]
2674
2675block1:
2676    return
2677",
2678        );
2679        assert_eq!(only(&text), "@f block0 switch: two cases of this switch have the same value");
2680    }
2681
2682    #[test]
2683    fn operands_that_do_not_agree_are_reported() {
2684        let text = wrap(
2685            "(i32, i64) -> i32",
2686            "block0(%0: i32, %1: i64):\n    %2 = add %0, %1\n    return %2\n",
2687        );
2688        assert_eq!(
2689            only(&text),
2690            "@f block0 add: the operands of add have one type and these are i32 and i64"
2691        );
2692    }
2693
2694    #[test]
2695    fn a_conversion_that_goes_the_wrong_way_is_reported() {
2696        let text = wrap("(i32) -> i64", "block0(%0: i32):\n    %1 = trunc.i64 %0\n    return %1\n");
2697        assert_eq!(
2698            only(&text),
2699            "@f block0 trunc: trunc produces something narrower and i32 to i64 is not"
2700        );
2701    }
2702
2703    #[test]
2704    fn a_bitcast_between_an_address_and_a_number_is_reported() {
2705        let text =
2706            wrap("(ptr) -> i64", "block0(%0: ptr):\n    %1 = bitcast.i64 %0\n    return %1\n");
2707        assert_eq!(
2708            only(&text),
2709            "@f block0 bitcast: a bitcast between a pointer and a number is ptrtoint or inttoptr"
2710        );
2711    }
2712
2713    #[test]
2714    fn an_operand_of_the_wrong_kind_is_reported() {
2715        let text = wrap("(i32) -> i32", "block0(%0: i32):\n    %1 = fadd %0, %0\n    return %1\n");
2716        assert_eq!(
2717            only(&text),
2718            "@f block0 fadd: operand 1 of fadd is a floating point value and this one is i32"
2719        );
2720    }
2721
2722    #[test]
2723    fn a_condition_that_is_not_one_bit_is_reported() {
2724        let text = wrap(
2725            "(i32)",
2726            "block0(%0: i32):
2727    br_if %0, block1, block1
2728
2729block1:
2730    return
2731",
2732        );
2733        assert_eq!(only(&text), "@f block0 br_if: br_if branches on an i1 and this one on i32");
2734    }
2735
2736    #[test]
2737    fn a_return_that_does_not_match_the_signature_is_reported() {
2738        let text = wrap("() -> i32", "block0:\n    return\n");
2739        assert_eq!(only(&text), "@f block0 return: the signature returns 1 and this returns 0");
2740    }
2741
2742    #[test]
2743    fn a_call_that_disagrees_with_the_declaration_is_reported() {
2744        let text = format!(
2745            "{HEADER}
2746func @g(i32, ...) -> i32, linkage(external);
2747
2748func @f(i32) -> i32, linkage(external) {{
2749block0(%0: i32):
2750    %1 = call @g(%0) : (i32) -> i32
2751    return %1
2752}}
2753"
2754        );
2755        assert_eq!(only(&text), "@f block0 call: @g is declared here with another signature");
2756    }
2757
2758    #[test]
2759    fn a_global_whose_image_is_not_its_size_is_reported() {
2760        let text =
2761            format!("{HEADER}\nglobal @x : bytes 8 = {{ i32 7 }}, align 4, linkage(external)\n");
2762        assert_eq!(only(&text), "@x: the image is 4 bytes and the global is 8");
2763    }
2764
2765    #[test]
2766    fn a_pointer_in_an_image_has_no_width_and_is_reported() {
2767        // What a `NULL` in a static initializer used to become. A `ptr` takes the width of the
2768        // target's addresses and a type says nothing about the target, so the datum measured
2769        // zero bytes and the value it held went nowhere.
2770        let text = format!(
2771            "{HEADER}\nglobal @x : bytes 8 = {{ ptr 0x0, zero 8 }}, align 8, linkage(external)\n"
2772        );
2773        assert_eq!(only(&text), "@x: a scalar in an image has a width and ptr has none");
2774    }
2775
2776    #[test]
2777    fn a_declaration_of_something_another_module_defines_may_be_constant() {
2778        // `extern const int x;` names an object in the library's read only data. Whether it may
2779        // be written through is a fact about the object rather than about who holds the bytes.
2780        let text = format!("{HEADER}\nglobal @x : bytes 4, align 4, linkage(external), constant\n");
2781        assert!(errors(&text).is_empty(), "{:?}", errors(&text));
2782    }
2783
2784    #[test]
2785    fn an_alias_to_itself_is_reported() {
2786        let text = format!("{HEADER}\nalias @a = @a, linkage(external)\n");
2787        assert_eq!(only(&text), "@a: an alias to itself");
2788    }
2789
2790    #[test]
2791    fn an_ifunc_that_does_not_resolve_through_a_function_is_reported() {
2792        let text = format!(
2793            "{HEADER}
2794global @g : i32 = 0, align 4, linkage(external)
2795
2796ifunc @f = @g, linkage(external)
2797"
2798        );
2799        assert_eq!(
2800            only(&text),
2801            "@f: an ifunc resolves through a function and this target is not one"
2802        );
2803    }
2804
2805    #[test]
2806    fn attributes_that_contradict_each_other_are_reported() {
2807        let text =
2808            format!("{HEADER}\nfunc @f(), linkage(external), attrs(always_inline, noinline);\n");
2809        assert_eq!(
2810            only(&text),
2811            "@f: `always_inline` and `noinline` cannot both be true of a function"
2812        );
2813    }
2814
2815    // The rest are things no text can say, because the parser turns them down before the
2816    // verifier would see them. They are what a pass produces, which is who the verifier is for.
2817
2818    fn one_error(module: &Module, func: &Func, names: &Interner) -> String {
2819        match verify_func(module, func, names) {
2820            Ok(()) => panic!("that was expected to be turned down"),
2821            Err(errors) => {
2822                assert_eq!(errors.len(), 1, "{errors:#?}");
2823                errors[0].to_string()
2824            }
2825        }
2826    }
2827
2828    #[test]
2829    fn a_value_the_function_does_not_have_is_reported() {
2830        let mut names = Interner::new();
2831        let module = Module::new(names.intern("built.c"), &target());
2832        let mut func = Func::new(names.intern("f"), Signature::new());
2833        let block = func.create_block();
2834        let args = func.push_values(&[Value::from_usize(9)]);
2835        let inst =
2836            func.create_inst(InstData { args, ..InstData::new(Opcode::Return) }, &[], Span::DUMMY);
2837        func.append_inst(block, inst);
2838        assert_eq!(
2839            one_error(&module, &func, &names),
2840            "@f block0 return: %9 is not a value of this function"
2841        );
2842    }
2843
2844    #[test]
2845    fn a_value_whose_definition_has_been_taken_out_is_reported() {
2846        let mut names = Interner::new();
2847        let module = Module::new(names.intern("built.c"), &target());
2848        let i32_ = Type::int(32);
2849        let mut func = Func::new(names.intern("f"), Signature::new().with_returns(&[i32_]));
2850        let block = func.create_block();
2851        let mut b = Builder::new(&mut func, block);
2852        let value = b.iconst(i32_, 7);
2853        b.ret(&[value]);
2854        let Def::Result { inst, .. } = func[value].def else { unreachable!("a constant") };
2855        func.remove_inst(inst);
2856        assert_eq!(
2857            one_error(&module, &func, &names),
2858            "@f block0 return: %0 is produced by an instruction that is not in the function"
2859        );
2860    }
2861
2862    #[test]
2863    fn an_instruction_carrying_another_opcodes_payload_is_reported() {
2864        // This one prints as text the parser cannot read back, so the round trip would fail on
2865        // it somewhere else entirely if the verifier did not say so here.
2866        let mut names = Interner::new();
2867        let module = Module::new(names.intern("built.c"), &target());
2868        let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
2869        let block = func.create_block();
2870        let param = func.append_param(block, Type::int(32));
2871        let args = func.push_values(&[param, param]);
2872        let inst = func.create_inst(
2873            InstData { args, extra: Extra::IntPred(IntPred::Eq), ..InstData::new(Opcode::Add) },
2874            &[Type::int(32)],
2875            Span::DUMMY,
2876        );
2877        func.append_inst(block, inst);
2878        let ret = func.create_inst(InstData::new(Opcode::Return), &[], Span::DUMMY);
2879        func.append_inst(block, ret);
2880        assert_eq!(
2881            one_error(&module, &func, &names),
2882            "@f block0 add: add carries nothing and this one carries an integer comparison"
2883        );
2884    }
2885
2886    #[test]
2887    fn a_metadata_node_that_is_its_own_parent_is_reported() {
2888        let mut names = Interner::new();
2889        let mut module = Module::new(names.intern("built.c"), &target());
2890        module.add_meta(MetaNode::Tbaa(TbaaNode {
2891            name: names.intern("int"),
2892            parent: Some(Idx::from_usize(0)),
2893            offset: 0,
2894        }));
2895        let found = match verify(&module, &names) {
2896            Ok(()) => panic!("that was expected to be turned down"),
2897            Err(errors) => errors,
2898        };
2899        assert_eq!(found.len(), 1, "{found:#?}");
2900        assert_eq!(
2901            found[0].to_string(),
2902            "!0: a metadata node names one that comes before it and this one is !0"
2903        );
2904    }
2905
2906    #[test]
2907    fn a_datalayout_the_target_does_not_imply_is_reported() {
2908        let mut names = Interner::new();
2909        let mut module = Module::new(names.intern("built.c"), &target());
2910        module.datalayout = DataLayout::parse("e-p:32:32-i64:64-f80:32-S64").expect("a layout");
2911        let found = match verify(&module, &names) {
2912            Ok(()) => panic!("that was expected to be turned down"),
2913            Err(errors) => errors,
2914        };
2915        assert_eq!(found.len(), 1, "{found:#?}");
2916        assert!(found[0].to_string().starts_with("@built.c: the datalayout is "), "{}", found[0]);
2917    }
2918
2919    #[test]
2920    fn a_flag_riding_along_where_it_is_read_is_not_reported() {
2921        let mut names = Interner::new();
2922        let module = Module::new(names.intern("built.c"), &target());
2923        let i32_ = Type::int(32);
2924        let mut func = Func::new(
2925            names.intern("f"),
2926            Signature::new().with_params(&[i32_]).with_returns(&[i32_]),
2927        );
2928        let block = func.create_block();
2929        let param = func.append_param(block, i32_);
2930        let mut b = Builder::new(&mut func, block);
2931        let sum = b.binary(Opcode::Add, param, param, Flags::NSW);
2932        b.ret(&[sum]);
2933        assert!(verify_func(&module, &func, &names).is_ok());
2934    }
2935
2936    #[test]
2937    fn a_declaration_is_checked_and_has_nothing_else_to_check() {
2938        let mut names = Interner::new();
2939        let module = Module::new(names.intern("built.c"), &target());
2940        let func = Func::new(Symbol::from_raw(0), Signature::new());
2941        assert!(func.is_declaration());
2942        assert!(verify_func(&module, &func, &names).is_ok());
2943    }
2944
2945    // The forms holding a capability that the verifier has to turn down, from
2946    // `spec/safe-memory/06-instrumentation.md` section 6.2.2. Six instructions make a capability
2947    // and every other way of getting one is on this list.
2948
2949    #[test]
2950    fn a_capability_parameter_is_reported() {
2951        let text = wrap(
2952            "(cap) -> i32",
2953            "block0(%0: cap):
2954    %1 = iconst.i32 0
2955    return %1
2956",
2957        );
2958        reports(&text, "parameter 1 is a capability and a capability does not cross a call");
2959    }
2960
2961    #[test]
2962    fn a_capability_result_is_reported() {
2963        let text = wrap(
2964            "(i32) -> cap",
2965            "block0(%0: i32):
2966    unreachable
2967",
2968        );
2969        reports(&text, "result 1 is a capability and a capability does not cross a call");
2970    }
2971
2972    #[test]
2973    fn a_capability_a_block_takes_is_a_capability_like_any_other() {
2974        // Two paths each hold one and they meet. This is what block parameters are for and there
2975        // is nothing about a capability that makes it the exception.
2976        let text = wrap(
2977            "(i1, ptr) -> i32",
2978            "block0(%0: i1, %1: ptr):
2979    %2 = cap_of %1
2980    br_if %0, block1(%2), block1(%2)
2981
2982block1(%3: cap):
2983    %4 = iconst.i32 0
2984    return %4
2985",
2986        );
2987        assert_eq!(errors(&text), Vec::<String>::new());
2988    }
2989
2990    #[test]
2991    fn an_instruction_that_is_not_a_cap_instruction_producing_one_is_reported() {
2992        let text = wrap(
2993            "(ptr) -> i32",
2994            "block0(%0: ptr):
2995    %1 = cap_of %0
2996    %2 = bitcast.cap %1
2997    %3 = iconst.i32 0
2998    return %3
2999",
3000        );
3001        reports(&text, "bitcast produces a capability and only the cap instructions do that");
3002    }
3003
3004    #[test]
3005    fn a_capability_where_a_pointer_belongs_is_reported() {
3006        let text = wrap(
3007            "(ptr) -> i32",
3008            "block0(%0: ptr):
3009    %1 = cap_of %0
3010    %2 = cap_store %1, %1
3011    %3 = iconst.i32 0
3012    return %3
3013",
3014        );
3015        reports(&text, "operand 1 of cap_store is a pointer and this one is cap");
3016    }
3017
3018    #[test]
3019    fn a_pointer_where_a_capability_belongs_is_reported() {
3020        let text = wrap(
3021            "(ptr) -> i32",
3022            "block0(%0: ptr):
3023    %1 = cap_store %0, %0
3024    %2 = iconst.i32 0
3025    return %2
3026",
3027        );
3028        reports(&text, "operand 2 of cap_store is a capability and this one is ptr");
3029    }
3030
3031    #[test]
3032    fn narrowing_by_an_offset_and_a_length_of_different_widths_is_reported() {
3033        let text = wrap(
3034            "(ptr, i64) -> i32",
3035            "block0(%0: ptr, %1: i64):
3036    %2 = cap_of %0
3037    %3 = iconst.i32 4
3038    %4 = cap_narrow %2, %1, %3
3039    %5 = iconst.i32 0
3040    return %5
3041",
3042        );
3043        reports(&text, "the operands of cap_narrow have one type and these are i64 and i32");
3044    }
3045
3046    #[test]
3047    fn an_extent_answered_in_a_different_width_from_the_limit_is_reported() {
3048        // The limit and the answer count the same bytes, so a pass that asked in one width and
3049        // read the answer in another has arithmetic that does not mean what it looks like.
3050        let text = wrap(
3051            "(ptr, i64) -> i32",
3052            "block0(%0: ptr, %1: i64):
3053    %2 = cap_of %0
3054    %3 = cap_extent.i32 %2, %0, %1
3055    return %3
3056",
3057        );
3058        reports(&text, "cap_extent produces i64 here and this one produces i32");
3059    }
3060
3061    #[test]
3062    fn a_capability_instruction_with_the_wrong_number_of_operands_is_reported() {
3063        let text = wrap(
3064            "(ptr) -> i32",
3065            "block0(%0: ptr):
3066    %1 = cap_of %0, %0
3067    %2 = iconst.i32 0
3068    return %2
3069",
3070        );
3071        reports(&text, "cap_of takes 1 operands and this one has 2");
3072    }
3073
3074    #[test]
3075    fn a_check_given_its_pointer_and_its_capability_the_other_way_round_is_reported() {
3076        let text = wrap(
3077            "(ptr) -> i32",
3078            "block0(%0: ptr):
3079    %1 = cap_of %0
3080    check_live %0, %1
3081    %2 = iconst.i32 0
3082    return %2
3083",
3084        );
3085        reports(&text, "operand 1 of check_live is a capability and this one is ptr");
3086        reports(&text, "operand 2 of check_live is a pointer and this one is cap");
3087    }
3088
3089    #[test]
3090    fn a_check_on_the_same_value_twice_is_reported() {
3091        // The one that would otherwise pass and mean nothing, since a pointer is within its
3092        // own bounds however wrong the bounds are.
3093        let text = wrap(
3094            "(ptr) -> i32",
3095            "block0(%0: ptr):
3096    check_bounds %0, %0, size 4, align 4
3097    %1 = iconst.i32 0
3098    return %1
3099",
3100        );
3101        reports(&text, "operand 1 of check_bounds is a capability and this one is ptr");
3102    }
3103
3104    #[test]
3105    fn a_bounds_check_over_a_length_that_is_not_a_number_is_reported() {
3106        // The third operand is how many bytes the check is about, which section 7.4's hoisted check
3107        // works out from a trip count, so it is a value and a value can be the wrong type.
3108        let text = wrap(
3109            "(ptr) -> i32",
3110            "block0(%0: ptr):
3111    %1 = cap_of %0
3112    check_bounds %1, %0, %0, size 4, align 4
3113    %2 = iconst.i32 0
3114    return %2
3115",
3116        );
3117        reports(&text, "operand 3 of check_bounds is an integer and this one is ptr");
3118    }
3119
3120    #[test]
3121    fn a_bounds_check_with_a_fourth_operand_is_reported() {
3122        let text = wrap(
3123            "(ptr) -> i32",
3124            "block0(%0: ptr):
3125    %1 = cap_of %0
3126    %2 = iconst.i64 4
3127    check_bounds %1, %0, %2, %2, size 4, align 4
3128    %3 = iconst.i32 0
3129    return %3
3130",
3131        );
3132        reports(&text, "check_bounds takes 2 or 3 operands and this one has 4");
3133    }
3134
3135    #[test]
3136    fn a_derivation_check_without_the_pointer_it_is_about_is_reported() {
3137        let text = wrap(
3138            "(ptr) -> i32",
3139            "block0(%0: ptr):
3140    %1 = cap_of %0
3141    %2 = iconst.i64 4
3142    check_deriv %1, %0, %2
3143    %3 = iconst.i32 0
3144    return %3
3145",
3146        );
3147        reports(&text, "check_deriv takes 4 operands and this one has 3");
3148    }
3149
3150    #[test]
3151    fn a_plane_write_over_a_length_that_is_not_a_number_is_reported() {
3152        // The length is an operand and not a payload because a variable length array has one
3153        // the front end cannot know, so it is a value and a value can be the wrong type.
3154        let text = wrap(
3155            "(ptr) -> i32",
3156            "block0(%0: ptr):
3157    meta_end %0, %0
3158    %1 = iconst.i32 0
3159    return %1
3160",
3161        );
3162        reports(&text, "operand 2 of meta_end is an integer and this one is ptr");
3163    }
3164
3165    #[test]
3166    fn a_region_marker_handed_a_value_is_reported() {
3167        // A region says something about the code between its two ends and nothing about any
3168        // value, so there is no operand it could be given that would mean anything.
3169        let text = wrap(
3170            "(ptr) -> i32",
3171            "block0(%0: ptr):
3172    safe_region_end %0
3173    %1 = iconst.i32 0
3174    return %1
3175",
3176        );
3177        reports(&text, "safe_region_end takes 0 operands and this one has 1");
3178    }
3179
3180    #[test]
3181    fn a_fact_about_something_that_is_not_a_pointer_is_reported() {
3182        // All four facts are about storage reached through a pointer, so there is nothing any of
3183        // them could mean about an integer.
3184        let text = wrap(
3185            "(i32) -> i32",
3186            "block0(%0: i32):
3187    return %0
3188
3189facts:
3190    %0 = !live
3191",
3192        );
3193        reports(&text, "%0 is said to be a pointer and it is i32");
3194    }
3195
3196    #[test]
3197    fn an_alignment_that_is_not_a_power_of_two_is_reported() {
3198        // Alignment is a power of two everywhere else in the IR and a fact is not the place to
3199        // start meaning something else by the word.
3200        let text = wrap(
3201            "(ptr) -> ptr",
3202            "block0(%0: ptr):
3203    return %0
3204
3205facts:
3206    %0 = !aligned(6)
3207",
3208        );
3209        reports(&text, "%0 is said to be aligned to 6 and an alignment is a power of two");
3210    }
3211
3212    #[test]
3213    fn a_range_that_starts_somewhere_that_is_not_a_pointer_is_reported() {
3214        // A range is a place, so where it starts is an address. A number there would compare
3215        // fine against another number and would be answering a different question.
3216        let text = wrap(
3217            "(ptr, i64) -> ptr",
3218            "block0(%0: ptr, %1: i64):
3219    return %0
3220
3221facts:
3222    %0 = !bounds(%1, %1)
3223",
3224        );
3225        reports(&text, "the range %0 is in starts at a pointer and %1 is i64");
3226    }
3227
3228    #[test]
3229    fn a_range_whose_extent_is_not_a_number_is_reported() {
3230        let text = wrap(
3231            "(ptr) -> ptr",
3232            "block0(%0: ptr):
3233    return %0
3234
3235facts:
3236    %0 = !bounds(%0, %0)
3237",
3238        );
3239        reports(&text, "the range %0 is in has an integer extent and %0 is ptr");
3240    }
3241
3242    #[test]
3243    fn a_range_computed_after_the_pointer_it_is_about_is_reported() {
3244        // The fact is about the value everywhere it is live, so a range named by something
3245        // computed later is a range half its uses could not name.
3246        let text = wrap(
3247            "(ptr, i64) -> ptr",
3248            "block0(%0: ptr, %1: i64):
3249    %2 = ptr_add %0, %1
3250    %3 = iconst.i64 16
3251    return %2
3252
3253facts:
3254    %2 = !bounds(%0, %3)
3255",
3256        );
3257        reports(&text, "%2 names %3 and does not reach it");
3258    }
3259
3260    #[test]
3261    fn a_load_handed_a_plane_entry_is_reported() {
3262        // The two tables share the one numbering, so the kind is the only thing that says a
3263        // reference is to the right node, and an aliasing query over `character` means nothing.
3264        let text = format!(
3265            "{HEADER}\nfunc @f(ptr) -> i32, linkage(external) {{\n\
3266             block0(%0: ptr):\n    %1 = load.i32 %0, align 4, tbaa !0\n    return %1\n}}\n\
3267             \n!0 = plane character\n"
3268        );
3269        reports(&text, "load names an aliasing node and !0 is a plane entry");
3270    }
3271
3272    #[test]
3273    fn a_type_check_handed_an_aliasing_node_is_reported() {
3274        let text = format!(
3275            "{HEADER}\nfunc @f(ptr) -> i32, linkage(external) {{\n\
3276             block0(%0: ptr):\n    %1 = cap_of %0\n    \
3277             check_type %1, %0, size 4, align 4, tbaa !0\n    %2 = iconst.i32 0\n    \
3278             return %2\n}}\n\n!0 = tbaa \"int\", offset 0\n"
3279        );
3280        reports(&text, "check_type names a plane entry and !0 is an aliasing node");
3281    }
3282
3283    #[test]
3284    fn a_plane_entry_for_a_byte_no_pointer_has_is_reported() {
3285        // Which byte of a pointer it is, so byte eight of an eight byte pointer is a plane the
3286        // runtime could never write.
3287        let text = format!("{HEADER}\n!0 = plane pointer_slot 9\n");
3288        reports(&text, "a pointer on this target is 8 bytes and this is byte 9");
3289    }
3290
3291    #[test]
3292    fn a_plane_entry_naming_another_plane_entry_is_reported() {
3293        let text = format!("{HEADER}\n!0 = plane character\n!1 = plane !0\n");
3294        reports(&text, "a plane entry names a type and !0 is a plane entry");
3295    }
3296
3297    #[test]
3298    fn a_null_capability_handed_an_operand_is_reported() {
3299        // It is the one capability instruction that describes nothing, so a pointer next to it
3300        // is a pointer somebody believed it was about.
3301        let text = wrap(
3302            "(ptr) -> i32",
3303            "block0(%0: ptr):
3304    %1 = cap_null %0
3305    %2 = iconst.i32 0
3306    return %2
3307",
3308        );
3309        reports(&text, "cap_null takes 0 operands and this one has 1");
3310    }
3311}