Skip to main content

rucc_ir/
opcode.rs

1//! The instruction set.
2//!
3//! Design: `spec/08-ir.md` section 8.3.
4//!
5//! The set is small enough to enumerate and it is closed. Adding an opcode is a spec change,
6//! because the verifier, the printer, the parser, the rewrite rules and the lowering all have
7//! to learn it, and an opcode that only half of them know about is a silent miscompilation
8//! waiting for the right input.
9//!
10//! Two things are deliberately absent. There is no `getelementptr`: pointer arithmetic is
11//! [`Opcode::PtrAdd`] over a byte offset the frontend computed, because C never needs the
12//! multi-index form and its absence removes a well known source of complexity. And there is no
13//! `phi`: values arriving at a block are the block's parameters, passed by the branch, so
14//! there is no operand list positionally tied to a predecessor list kept somewhere else.
15
16use std::fmt;
17
18/// One instruction of the IR.
19///
20/// The names are the textual form exactly, so [`Opcode::name`] and [`Opcode::from_name`] are
21/// what the printer and the parser use, and neither carries a table of its own that could
22/// drift from this one.
23///
24/// The enum is not `non_exhaustive`, deliberately. The set is closed, so a pass that matches
25/// on every opcode should stop compiling when one is added rather than fall into a wildcard
26/// arm that quietly does the wrong thing.
27#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub enum Opcode {
29    // Constants. A constant is an instruction rather than an operand kind, so that every
30    // operand is a value and every value has one definition, which is what makes the
31    // dominance check in the verifier a single rule rather than a rule with exceptions.
32    /// An integer constant, `iconst.i32 7`.
33    IConst,
34    /// A floating point constant, `fconst.f64 0x1.8p+1`.
35    FConst,
36    /// A vector constant with every lane the same, `splat.i8x16 0`.
37    Splat,
38    /// The address of a global or a function, `global_addr @counter`.
39    GlobalAddr,
40    /// The address of a block in this function, `block_addr block3`.
41    ///
42    /// The one instruction that names a block without being a branch, which is what GNU's
43    /// `&&label` is. Where it goes is [`Opcode::IndirectBr`], and the two are only useful
44    /// together: an address on its own is a number that nothing can do anything with.
45    BlockAddr,
46
47    // Arithmetic.
48    /// Integer addition.
49    Add,
50    /// Integer subtraction.
51    Sub,
52    /// Integer multiplication.
53    Mul,
54    /// Signed division.
55    SDiv,
56    /// Unsigned division.
57    UDiv,
58    /// Signed remainder, with the sign of the dividend.
59    SRem,
60    /// Unsigned remainder.
61    URem,
62    /// Bitwise and.
63    And,
64    /// Bitwise or.
65    Or,
66    /// Bitwise exclusive or.
67    Xor,
68    /// Shift left.
69    Shl,
70    /// Logical shift right, shifting in zeroes.
71    LShr,
72    /// Arithmetic shift right, shifting in the sign bit.
73    AShr,
74    /// Floating point addition.
75    FAdd,
76    /// Floating point subtraction.
77    FSub,
78    /// Floating point multiplication.
79    FMul,
80    /// Floating point division.
81    FDiv,
82    /// Floating point remainder.
83    FRem,
84    /// Floating point negation, which flips the sign bit and is not `0 - x`.
85    FNeg,
86    /// Fused multiply-add, rounded once.
87    Fma,
88
89    // Comparison.
90    /// Integer comparison, producing `i1` or a vector of `i1`.
91    ICmp,
92    /// Floating point comparison, producing `i1` or a vector of `i1`.
93    FCmp,
94
95    // Selection.
96    /// One of two values, chosen by a bit. `select c, a, b` is `a` when `c` is one.
97    ///
98    /// This is what control flow becomes when it stops being control flow.
99    /// `spec/optimizer/22-phiopt-and-if-conversion.md` section 22.2 makes it the lowering target
100    /// for a diamond whose two arms compute a value, and the reason it is an opcode rather than a
101    /// pattern is that it is the form the rule set is written against: `select(c, a, a) -> a` and
102    /// `select(c, 1, 0) -> zext(c)` are ordinary rules once the shape has a name.
103    ///
104    /// Both arms are evaluated, which is the whole point and also the whole danger. Whatever
105    /// produces one of these owes the argument that evaluating the arm that is not chosen is
106    /// harmless, and section 22.6 is the list of ways that argument goes wrong.
107    Select,
108
109    // Conversion.
110    /// Narrows an integer, discarding the high bits.
111    Trunc,
112    /// Widens an integer, copying the sign bit.
113    SExt,
114    /// Widens an integer, filling with zeroes.
115    ZExt,
116    /// Narrows a floating point value.
117    FPTrunc,
118    /// Widens a floating point value.
119    FPExt,
120    /// Floating point to signed integer.
121    FPToSI,
122    /// Floating point to unsigned integer.
123    FPToUI,
124    /// Signed integer to floating point.
125    SIToFP,
126    /// Unsigned integer to floating point.
127    UIToFP,
128    /// An address to an integer of the same width.
129    PtrToInt,
130    /// An integer to an address.
131    IntToPtr,
132    /// A reinterpretation of the same bits at the same width.
133    Bitcast,
134
135    // Memory.
136    /// Memory as the function found it, which is where a memory SSA chain starts.
137    ///
138    /// It produces one `mem` and takes nothing, and it belongs at the top of the entry block.
139    /// GCC calls the same thing the default definition of `.MEM` and LLVM calls it
140    /// `liveOnEntry`. It exists as an instruction rather than as a parameter of the entry block
141    /// because the entry block's parameters are the function's parameters and the verifier
142    /// checks them against the signature, and memory is not an argument anybody passed.
143    MemEntry,
144    /// A stack slot. In the entry block, or marked dynamic for a variable length array.
145    Alloca,
146    /// A read.
147    Load,
148    /// A write, producing no value.
149    Store,
150    /// Address arithmetic: an address and a byte offset.
151    PtrAdd,
152    /// A copy of a known size between addresses that do not overlap.
153    Memcpy,
154    /// A copy of a known size between addresses that may overlap.
155    Memmove,
156    /// A fill of a known size with one byte.
157    Memset,
158    /// An atomic read.
159    AtomicLoad,
160    /// An atomic write.
161    AtomicStore,
162    /// An atomic read-modify-write, carrying which operation in [`RmwOp`](crate::RmwOp).
163    AtomicRmw,
164    /// An atomic compare and exchange, producing the old value and whether it succeeded.
165    Cmpxchg,
166    /// A memory barrier.
167    Fence,
168
169    // Memory safety. Design: `spec/safe-memory/06-instrumentation.md` section 6.2.2. None of
170    // these is emitted unless `-fsafety` asked for it, and a function compiled without it
171    // contains not one of them.
172    /// The capability of a pointer value, taken from the pointer's provenance.
173    CapOf,
174    /// The capability in the auxiliary slot beside a stored pointer, read back.
175    ///
176    /// A pointer written to memory and read again has to bring its capability with it, and where
177    /// the capability lives is document 05's question rather than this one's. What this says is
178    /// that a capability comes back from an address, which is enough for every pass above.
179    CapLoad,
180    /// The other half of [`Opcode::CapLoad`], writing one into the slot beside a pointer.
181    CapStore,
182    /// The capability that permits nothing, which is what a null pointer has.
183    CapNull,
184    /// A capability narrowed to a sub-object of what it covered.
185    ///
186    /// Only under `-fsafety-subobject`. Narrowing is what catches an overflow from one member of
187    /// a struct into the next, and it is separate because C code that walks off the end of a
188    /// member on purpose exists and a project has to be able to say so.
189    CapNarrow,
190    /// The capability for an address that arrived from outside, recovered from the planes.
191    CapRecover,
192    /// How many bytes from an address on the capability covers, asking for no more than a limit.
193    ///
194    /// Three operands, the capability, the address and how many bytes the asker wants, and one
195    /// integer result that is never more than that limit. It is what
196    /// `spec/safe-memory/07-check-elimination.md` section 7.4 needs to split a loop: the checked
197    /// part and the unchecked part are divided at `min(n, extent / sizeof(T))`, and the extent is
198    /// the half of that a compiler cannot work out on its own.
199    ///
200    /// The limit is an operand because under milestone S1 answering means walking the lifetime
201    /// plane, and a walk that stops at the number of bytes the loop was going to read anyway is
202    /// bounded by the work the loop is already doing. An answer smaller than the truth costs
203    /// iterations in the checked half and is never wrong, which is what makes stopping early
204    /// allowed. Once a capability carries its own bounds, which is milestone S2, this is a
205    /// subtraction on the capability and the limit is one `min`.
206    CapExtent,
207    /// How many bytes below an address on the capability covers, asking for no more than a limit.
208    ///
209    /// The mirror of [`Opcode::CapExtent`], with the same three operands and the same kind of
210    /// answer. What it counts is the bytes ending at the address rather than the bytes starting
211    /// there, so an answer of `n` says that `[addr - n, addr)` belongs to one thing. The address
212    /// itself is one past what is asked about, which is what a walk from high to low needs: the
213    /// question is about where the walk ends up, and where it ends up is below where it began.
214    ///
215    /// The ownership asked about is the byte below the address rather than the byte at it, since
216    /// the address may be one past the end of the object and the object is what the question is
217    /// about. Everything else, the limit operand and why an answer short of the truth is allowed,
218    /// is [`Opcode::CapExtent`]'s.
219    CapExtentBack,
220    /// An access is within its capability's bounds, aligned, and permitted.
221    ///
222    /// The size and the alignment are the access's, and they are in the memory payload rather
223    /// than in operands because they are what the front end knew and not what the program
224    /// computed.
225    ///
226    /// A third operand overrides how many bytes are asked about, and it exists for the one check
227    /// the front end did not write. `spec/safe-memory/07-check-elimination.md` section 7.4 replaces
228    /// the checks in a loop that runs `n` times with one check over `n * sizeof(T)` bytes, and that
229    /// is a length the program computes rather than one anybody knew when the access was parsed. The
230    /// payload still holds the alignment and the type information of the access the check came from,
231    /// and its size becomes the size of one of them rather than the size of the question.
232    CheckBounds,
233    /// The capability's provenance is still live.
234    CheckLive,
235    /// The access agrees with the type plane, which is the effective type rule of C 6.5.
236    CheckType,
237    /// The bytes the access reads have been written.
238    CheckInit,
239    /// A pointer derived from another stays inside the capability the first one had.
240    ///
241    /// Three operands, because the answer is about the new pointer and the question is about
242    /// the old one's capability.
243    CheckDeriv,
244    /// The metadata this access is about to consult has not been changed under it.
245    CheckRace,
246    /// A storage instance begins here, over a range, with a class.
247    ///
248    /// Judgement J4. This is the `alloca` for an automatic instance and the allocator's report
249    /// for an allocated one, and the range is a pointer and a length in registers rather than a
250    /// payload, because the length of a variable length array is not known when the instruction
251    /// is written down.
252    MetaBegin,
253    /// A storage instance ends here, which is judgement J5.
254    ///
255    /// Every capability for it fails from this point on and keeps failing after the address is
256    /// handed out again, which is what makes the check a use after free check rather than a use
257    /// after reallocation one.
258    MetaEnd,
259    /// The effective type of a range is now this one.
260    MetaType,
261    /// The bytes of a range are now initialized.
262    MetaInit,
263    /// A range leaves the monitor's authority, or comes back, which is judgement J7.
264    MetaTransfer,
265    /// A declared exemption starts here, with the reason it was declared.
266    ///
267    /// Not an optimization hint. Everything between this and its `safe_region_end` is code the
268    /// monitor is told not to judge, so the reason it carries is a trust set entry, and
269    /// `spec/safe-memory/10-boundaries.md` section 10.2 counts them per build precisely so that
270    /// a reviewer can read what a binary's guarantee rests on.
271    SafeRegionBegin,
272    /// The end of the region the last `safe_region_begin` opened.
273    SafeRegionEnd,
274
275    // Control. Every one of these is a terminator.
276    /// An unconditional branch, `jump block1(%a, %b)`.
277    Jump,
278    /// A two-way branch on an `i1`.
279    BrIf,
280    /// A multi-way branch on an integer, with a default.
281    Switch,
282    /// A branch to an address, `indirect_br %0, block1, block2`.
283    ///
284    /// The targets are every block control can arrive at, which is what makes the edges of a
285    /// computed `goto` ordinary edges: nothing else in the compiler has to know that the
286    /// address decides which one it is. A target that is not listed is a branch that does not
287    /// happen, so a frontend that leaves one out has made a promise on the program's behalf.
288    IndirectBr,
289    /// A return, with the values the signature says.
290    Return,
291    /// A place control cannot reach, which the frontend emits after a `noreturn` call.
292    Unreachable,
293
294    // Calls.
295    /// A call to a named function.
296    Call,
297    /// A call through an address, carrying the signature it is called with.
298    CallIndirect,
299    /// A call in tail position that reuses the frame, which is a terminator.
300    TailCall,
301
302    // Intrinsics, which is the closed part. The open part is `TargetIntrinsic`.
303    /// Count leading zeroes.
304    Ctlz,
305    /// Count trailing zeroes.
306    Cttz,
307    /// Count set bits.
308    Ctpop,
309    /// Reverse the bytes.
310    Bswap,
311    /// Reverse the bits.
312    Bitreverse,
313    /// Signed addition, producing the result and whether it overflowed.
314    SAddOverflow,
315    /// Unsigned addition, producing the result and whether it overflowed.
316    UAddOverflow,
317    /// Signed subtraction, producing the result and whether it overflowed.
318    SSubOverflow,
319    /// Unsigned subtraction, producing the result and whether it overflowed.
320    USubOverflow,
321    /// Signed multiplication, producing the result and whether it overflowed.
322    SMulOverflow,
323    /// Unsigned multiplication, producing the result and whether it overflowed.
324    UMulOverflow,
325    /// `__builtin_expect`, which is the value with a hint attached.
326    Expect,
327    /// `__builtin_unreachable` as a hint on a path, distinct from the terminator.
328    UnreachableHint,
329    /// `__builtin_prefetch`.
330    Prefetch,
331    /// `__builtin_frame_address`.
332    FrameAddress,
333    /// `__builtin_return_address`.
334    ReturnAddress,
335    /// The start of a variable argument list.
336    VaStart,
337    /// One argument off a variable argument list, which moves the list on as it reads it. Two
338    /// of these on one list are two arguments and never one argument read twice, so whatever
339    /// decides which instructions may be folded together has to leave these alone.
340    VaArg,
341    /// One argument off a variable argument list, when that argument is an object rather than a
342    /// value, which is what a `struct` or a `union` read out of one is.
343    ///
344    /// It answers the address of the object rather than the object, because an aggregate is not
345    /// a value and there is nothing for one result to be. Where the object arrives in registers
346    /// there is no address until something makes one, so what this asks of a target is a place
347    /// to put the registers and the address of that place, which is the copy every psABI's own
348    /// description of the algorithm makes. It moves the list on for the reason [`Opcode::VaArg`]
349    /// does.
350    VaObject,
351    /// The end of a variable argument list.
352    VaEnd,
353    /// A copy of a variable argument list.
354    VaCopy,
355    /// The stack pointer, saved before a variable length array.
356    StackSave,
357    /// The stack pointer, restored after one.
358    StackRestore,
359    /// The marker a `setjmp` leaves, which pins everything live across it.
360    SetjmpMarker,
361    /// The marker a `longjmp` leaves.
362    LongjmpMarker,
363    /// A target-specific intrinsic, named rather than enumerated, for the vector builtins.
364    TargetIntrinsic,
365
366    /// Inline assembly. A terminator when it has labels, which is `asm goto`.
367    InlineAsm,
368}
369
370impl Opcode {
371    /// The textual form, which is also what the parser reads.
372    #[must_use]
373    pub const fn name(self) -> &'static str {
374        match self {
375            Self::IConst => "iconst",
376            Self::FConst => "fconst",
377            Self::Splat => "splat",
378            Self::GlobalAddr => "global_addr",
379            Self::BlockAddr => "block_addr",
380            Self::Add => "add",
381            Self::Sub => "sub",
382            Self::Mul => "mul",
383            Self::SDiv => "sdiv",
384            Self::UDiv => "udiv",
385            Self::SRem => "srem",
386            Self::URem => "urem",
387            Self::And => "and",
388            Self::Or => "or",
389            Self::Xor => "xor",
390            Self::Shl => "shl",
391            Self::LShr => "lshr",
392            Self::AShr => "ashr",
393            Self::FAdd => "fadd",
394            Self::FSub => "fsub",
395            Self::FMul => "fmul",
396            Self::FDiv => "fdiv",
397            Self::FRem => "frem",
398            Self::FNeg => "fneg",
399            Self::Fma => "fma",
400            Self::ICmp => "icmp",
401            Self::FCmp => "fcmp",
402            Self::Select => "select",
403            Self::Trunc => "trunc",
404            Self::SExt => "sext",
405            Self::ZExt => "zext",
406            Self::FPTrunc => "fptrunc",
407            Self::FPExt => "fpext",
408            Self::FPToSI => "fptosi",
409            Self::FPToUI => "fptoui",
410            Self::SIToFP => "sitofp",
411            Self::UIToFP => "uitofp",
412            Self::PtrToInt => "ptrtoint",
413            Self::IntToPtr => "inttoptr",
414            Self::Bitcast => "bitcast",
415            Self::MemEntry => "mem_entry",
416            Self::Alloca => "alloca",
417            Self::Load => "load",
418            Self::Store => "store",
419            Self::PtrAdd => "ptr_add",
420            Self::Memcpy => "memcpy",
421            Self::Memmove => "memmove",
422            Self::Memset => "memset",
423            Self::AtomicLoad => "atomic_load",
424            Self::AtomicStore => "atomic_store",
425            Self::AtomicRmw => "atomic_rmw",
426            Self::Cmpxchg => "cmpxchg",
427            Self::Fence => "fence",
428            Self::CapOf => "cap_of",
429            Self::CapLoad => "cap_load",
430            Self::CapStore => "cap_store",
431            Self::CapNull => "cap_null",
432            Self::CapNarrow => "cap_narrow",
433            Self::CapRecover => "cap_recover",
434            Self::CapExtent => "cap_extent",
435            Self::CapExtentBack => "cap_extent_back",
436            Self::CheckBounds => "check_bounds",
437            Self::CheckLive => "check_live",
438            Self::CheckType => "check_type",
439            Self::CheckInit => "check_init",
440            Self::CheckDeriv => "check_deriv",
441            Self::CheckRace => "check_race",
442            Self::MetaBegin => "meta_begin",
443            Self::MetaEnd => "meta_end",
444            Self::MetaType => "meta_type",
445            Self::MetaInit => "meta_init",
446            Self::MetaTransfer => "meta_transfer",
447            Self::SafeRegionBegin => "safe_region_begin",
448            Self::SafeRegionEnd => "safe_region_end",
449            Self::Jump => "jump",
450            Self::BrIf => "br_if",
451            Self::Switch => "switch",
452            Self::IndirectBr => "indirect_br",
453            Self::Return => "return",
454            Self::Unreachable => "unreachable",
455            Self::Call => "call",
456            Self::CallIndirect => "call_indirect",
457            Self::TailCall => "tail_call",
458            Self::Ctlz => "ctlz",
459            Self::Cttz => "cttz",
460            Self::Ctpop => "ctpop",
461            Self::Bswap => "bswap",
462            Self::Bitreverse => "bitreverse",
463            Self::SAddOverflow => "sadd_overflow",
464            Self::UAddOverflow => "uadd_overflow",
465            Self::SSubOverflow => "ssub_overflow",
466            Self::USubOverflow => "usub_overflow",
467            Self::SMulOverflow => "smul_overflow",
468            Self::UMulOverflow => "umul_overflow",
469            Self::Expect => "expect",
470            Self::UnreachableHint => "unreachable_hint",
471            Self::Prefetch => "prefetch",
472            Self::FrameAddress => "frame_address",
473            Self::ReturnAddress => "return_address",
474            Self::VaStart => "va_start",
475            Self::VaArg => "va_arg",
476            Self::VaObject => "va_object",
477            Self::VaEnd => "va_end",
478            Self::VaCopy => "va_copy",
479            Self::StackSave => "stacksave",
480            Self::StackRestore => "stackrestore",
481            Self::SetjmpMarker => "setjmp_marker",
482            Self::LongjmpMarker => "longjmp_marker",
483            Self::TargetIntrinsic => "target_intrinsic",
484            Self::InlineAsm => "inline_asm",
485        }
486    }
487
488    /// Every opcode, in the order they are declared.
489    ///
490    /// The parser walks this rather than holding a second table, because a second table is a
491    /// table that can disagree with the first one.
492    pub fn all() -> impl Iterator<Item = Self> {
493        ALL.iter().copied()
494    }
495
496    /// The opcode with that name, if there is one.
497    #[must_use]
498    pub fn from_name(name: &str) -> Option<Self> {
499        ALL.iter().copied().find(|op| op.name() == name)
500    }
501
502    /// Whether this ends a block.
503    ///
504    /// [`Opcode::InlineAsm`] is not here and is the one instruction whose answer depends on
505    /// the instruction rather than on the opcode: `asm goto` has successors and everything
506    /// else does not. Ask the instruction, not the opcode.
507    #[must_use]
508    pub const fn is_terminator(self) -> bool {
509        matches!(
510            self,
511            Self::Jump
512                | Self::BrIf
513                | Self::Switch
514                | Self::IndirectBr
515                | Self::Return
516                | Self::Unreachable
517                | Self::TailCall
518        )
519    }
520
521    /// Whether the operands can be swapped without changing the result.
522    ///
523    /// The floating point cases are commutative even under the strictest rounding, because
524    /// swapping the operands of an addition does not change which of them is a NaN, and the
525    /// sign of a NaN result is not something we promise anything about either way.
526    #[must_use]
527    pub const fn is_commutative(self) -> bool {
528        matches!(
529            self,
530            Self::Add
531                | Self::Mul
532                | Self::And
533                | Self::Or
534                | Self::Xor
535                | Self::FAdd
536                | Self::FMul
537                | Self::SAddOverflow
538                | Self::UAddOverflow
539                | Self::SMulOverflow
540                | Self::UMulOverflow
541        )
542    }
543
544    /// Whether this reads or writes memory, or has an effect the optimizer has to preserve.
545    ///
546    /// An instruction that answers no can be deleted when nothing uses its result, moved
547    /// across a call, and merged with another one computing the same thing. Everything else
548    /// has to be argued about individually, so the conservative answer is the true one here
549    /// and the list of exceptions is the part that is checked.
550    #[must_use]
551    pub const fn has_effects(self) -> bool {
552        !matches!(
553            self,
554            Self::IConst
555                | Self::FConst
556                | Self::Splat
557                | Self::GlobalAddr
558                | Self::BlockAddr
559                | Self::Add
560                | Self::Sub
561                | Self::Mul
562                | Self::SDiv
563                | Self::UDiv
564                | Self::SRem
565                | Self::URem
566                | Self::And
567                | Self::Or
568                | Self::Xor
569                | Self::Shl
570                | Self::LShr
571                | Self::AShr
572                | Self::FAdd
573                | Self::FSub
574                | Self::FMul
575                | Self::FDiv
576                | Self::FRem
577                | Self::FNeg
578                | Self::Fma
579                | Self::ICmp
580                | Self::FCmp
581                | Self::Select
582                | Self::Trunc
583                | Self::SExt
584                | Self::ZExt
585                | Self::FPTrunc
586                | Self::FPExt
587                | Self::FPToSI
588                | Self::FPToUI
589                | Self::SIToFP
590                | Self::UIToFP
591                | Self::PtrToInt
592                | Self::IntToPtr
593                | Self::Bitcast
594                | Self::PtrAdd
595                | Self::Ctlz
596                | Self::Cttz
597                | Self::Ctpop
598                | Self::Bswap
599                | Self::Bitreverse
600                | Self::SAddOverflow
601                | Self::UAddOverflow
602                | Self::SSubOverflow
603                | Self::USubOverflow
604                | Self::SMulOverflow
605                | Self::UMulOverflow
606                | Self::Expect
607                | Self::FrameAddress
608                | Self::ReturnAddress
609                | Self::MemEntry
610                // Three of the capability instructions are arithmetic on a pointer's
611                // provenance and touch nothing. The other three do: `cap_load` and
612                // `cap_store` are an access, and `cap_recover` reads the planes.
613                | Self::CapOf
614                | Self::CapNull
615                | Self::CapNarrow
616        )
617    }
618
619    /// Whether an instruction with this opcode touches memory.
620    ///
621    /// This is what decides whether it takes a memory operand once memory SSA is built, per
622    /// document 09 of `spec/optimizer`. It is written as the exceptions to touching memory
623    /// rather than as a list of what does, for the reason document 08.6 gives about the escape
624    /// analysis: an opcode added later has to end up on the conservative side by default, and a
625    /// list of what touches memory would silently leave a new one out.
626    ///
627    /// `mem_entry` answers no. It produces memory rather than touching it, which is the whole
628    /// of what it is for.
629    #[must_use]
630    pub const fn touches_memory(self) -> bool {
631        if !self.has_effects() {
632            return false;
633        }
634        !matches!(
635            self,
636            // Fresh storage nothing could have been reading, and the pointer that names it.
637            Self::Alloca
638                // The stack pointer, which is a register and not memory. Putting it back is a
639                // different matter and is below, because it takes storage away.
640                | Self::StackSave
641                // Control, which goes somewhere rather than touching anything. A tail call is
642                // not here, because it is a call.
643                | Self::Jump
644                | Self::BrIf
645                | Self::Switch
646                | Self::IndirectBr
647                | Self::Return
648                | Self::Unreachable
649                | Self::UnreachableHint
650        )
651    }
652
653    /// Whether an instruction with this opcode writes memory, and so produces a new version of
654    /// it rather than only reading the version it was given.
655    ///
656    /// Everything that touches memory writes it except the ones that plainly do not. A `fence`
657    /// writes nothing and is still a write here, because document 09.5 says an atomic or a
658    /// barrier is a definition nothing walks past, and giving it one is how that is expressed
659    /// in a representation whose only ordering is the memory chain.
660    ///
661    /// The checks read the planes and change nothing, which
662    /// `spec/safe-memory/06-instrumentation.md` section 6.2.4 states as the word `readonly`. A
663    /// check that trapped is a program that stopped and there is no version of memory after it
664    /// for anything to observe, so the trap costs nothing here. What it does cost is that a
665    /// check may not be moved across a plane write, and that is the memory chain saying so
666    /// rather than this.
667    #[must_use]
668    pub const fn writes_memory(self) -> bool {
669        self.touches_memory()
670            && !matches!(
671                self,
672                Self::Load
673                    | Self::AtomicLoad
674                    | Self::Prefetch
675                    | Self::CapLoad
676                    | Self::CapRecover
677                    | Self::CapExtent
678                    | Self::CapExtentBack
679                    | Self::CheckBounds
680                    | Self::CheckLive
681                    | Self::CheckType
682                    | Self::CheckInit
683                    | Self::CheckDeriv
684                    | Self::CheckRace
685            )
686    }
687
688    /// How many values this produces, for the opcodes where the count is fixed.
689    ///
690    /// `None` means the count comes from somewhere else: a call takes it from its signature,
691    /// and inline assembly takes it from its output constraints. A tail call is not one of
692    /// them, because whatever it returns goes straight out of the function and there is no
693    /// instruction after it to use anything.
694    #[must_use]
695    pub const fn results(self) -> Option<u8> {
696        match self {
697            Self::Call | Self::CallIndirect | Self::InlineAsm => None,
698            Self::Cmpxchg
699            | Self::SAddOverflow
700            | Self::UAddOverflow
701            | Self::SSubOverflow
702            | Self::USubOverflow
703            | Self::SMulOverflow
704            | Self::UMulOverflow => Some(2),
705            Self::Store
706            | Self::Memcpy
707            | Self::Memmove
708            | Self::Memset
709            | Self::AtomicStore
710            | Self::Fence
711            | Self::Prefetch
712            | Self::VaStart
713            | Self::VaEnd
714            | Self::VaCopy
715            | Self::StackRestore
716            | Self::UnreachableHint
717            | Self::SetjmpMarker
718            | Self::LongjmpMarker
719            | Self::CapStore
720            | Self::CheckBounds
721            | Self::CheckLive
722            | Self::CheckType
723            | Self::CheckInit
724            | Self::CheckDeriv
725            | Self::CheckRace
726            | Self::MetaBegin
727            | Self::MetaEnd
728            | Self::MetaType
729            | Self::MetaInit
730            | Self::MetaTransfer
731            | Self::SafeRegionBegin
732            | Self::SafeRegionEnd => Some(0),
733            _ if self.is_terminator() => Some(0),
734            _ => Some(1),
735        }
736    }
737
738    /// Whether an instruction with this opcode produces a capability.
739    ///
740    /// Five of the eight `cap` instructions. The other three consume one instead: `cap_store`
741    /// writes it beside a pointer, and `cap_extent` and `cap_extent_back` ask it a question about
742    /// itself and answer with a number. The reason this is a question about the opcode rather than
743    /// about the
744    /// result type is that the verifier asks it the other way round: it walks the results looking
745    /// for a `cap` and needs to know whether the instruction under it was entitled to make one.
746    #[must_use]
747    pub const fn makes_capability(self) -> bool {
748        matches!(
749            self,
750            Self::CapOf | Self::CapLoad | Self::CapNull | Self::CapNarrow | Self::CapRecover
751        )
752    }
753
754    /// Which payload an instruction with this opcode carries.
755    ///
756    /// The printer reads the payload it finds and does not need this. The parser has only the
757    /// opcode when it reaches the operands, so this is where the two of them agree on what
758    /// comes after them. An instruction carrying a payload of some other kind prints as text
759    /// the parser cannot read back, which is why the verifier checks it against
760    /// [`Extra::kind`](crate::Extra::kind) rather than leaving it to be found later.
761    #[must_use]
762    pub const fn extra_kind(self) -> ExtraKind {
763        match self {
764            Self::IConst | Self::FConst | Self::Splat => ExtraKind::Imm,
765            Self::GlobalAddr | Self::TargetIntrinsic => ExtraKind::Symbol,
766            Self::ICmp => ExtraKind::IntPred,
767            Self::FCmp => ExtraKind::FloatPred,
768            Self::Alloca
769            | Self::Load
770            | Self::Store
771            | Self::Memcpy
772            | Self::Memmove
773            | Self::Memset
774            | Self::AtomicLoad
775            | Self::AtomicStore
776            | Self::Cmpxchg
777            // Three of the checks are about a run of bytes and the payload is where the size
778            // of that run is, along with the alignment `check_bounds` wants and the aliasing
779            // node `check_type` compares against. The other three ask a question about a
780            // pointer and not about a range, so they carry nothing.
781            | Self::CheckBounds
782            | Self::CheckType
783            | Self::CheckInit => ExtraKind::Mem,
784            // The plane writes. What each one needs beyond the range is different, and the range
785            // itself is operands, since the length of a variable length array is a value.
786            Self::MetaBegin => ExtraKind::Class,
787            Self::MetaTransfer => ExtraKind::Owner,
788            Self::MetaType => ExtraKind::Node,
789            Self::SafeRegionBegin => ExtraKind::Reason,
790            Self::VaObject => ExtraKind::VaObject,
791            Self::AtomicRmw => ExtraKind::Rmw,
792            Self::Fence => ExtraKind::Order,
793            Self::Jump | Self::BrIf | Self::BlockAddr | Self::IndirectBr => ExtraKind::Targets,
794            Self::Switch => ExtraKind::Switch,
795            Self::Call | Self::CallIndirect | Self::TailCall => ExtraKind::Call,
796            Self::InlineAsm => ExtraKind::Asm,
797            _ => ExtraKind::None,
798        }
799    }
800}
801
802/// Which of [`Extra`](crate::Extra)'s shapes an instruction carries.
803///
804/// The same list of names, without any of the payloads, so that a question about an opcode can
805/// be answered without an instruction to look at.
806#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
807pub enum ExtraKind {
808    /// Nothing.
809    None,
810    /// A constant.
811    Imm,
812    /// A name.
813    Symbol,
814    /// An integer comparison predicate.
815    IntPred,
816    /// A floating point comparison predicate.
817    FloatPred,
818    /// An access.
819    Mem,
820    /// An atomic read-modify-write.
821    Rmw,
822    /// A barrier's ordering.
823    Order,
824    /// Branch targets.
825    Targets,
826    /// A call.
827    Call,
828    /// A `switch`.
829    Switch,
830    /// Inline assembly.
831    Asm,
832    /// An object read off a variable argument list.
833    VaObject,
834    /// What kind of storage an instance is.
835    Class,
836    /// Who a range of memory went to.
837    Owner,
838    /// A metadata node.
839    Node,
840    /// Why a declared exemption is there.
841    Reason,
842}
843
844impl ExtraKind {
845    /// What it is, in words, for a message that names two of them and has to read as English.
846    #[must_use]
847    pub const fn name(self) -> &'static str {
848        match self {
849            Self::None => "nothing",
850            Self::Imm => "a constant",
851            Self::Symbol => "a name",
852            Self::IntPred => "an integer comparison",
853            Self::FloatPred => "a floating point comparison",
854            Self::Mem => "an access",
855            Self::Rmw => "a read-modify-write",
856            Self::Order => "an ordering",
857            Self::Targets => "branch targets",
858            Self::Call => "a call",
859            Self::Switch => "a switch",
860            Self::Asm => "inline assembly",
861            Self::VaObject => "an object off a variable argument list",
862            Self::Class => "a storage class",
863            Self::Owner => "an owner",
864            Self::Node => "a metadata node",
865            Self::Reason => "a reason",
866        }
867    }
868}
869
870impl fmt::Display for Opcode {
871    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
872        f.write_str(self.name())
873    }
874}
875
876/// Every opcode, which is what [`Opcode::all`] hands out.
877///
878/// This is written out rather than derived, and the test below is what keeps it complete: it
879/// checks the count against [`Opcode::InlineAsm`], the last variant, so a new opcode that is
880/// not added here fails the build rather than going quietly missing from the parser.
881static ALL: &[Opcode] = &[
882    Opcode::IConst,
883    Opcode::FConst,
884    Opcode::Splat,
885    Opcode::GlobalAddr,
886    Opcode::BlockAddr,
887    Opcode::Add,
888    Opcode::Sub,
889    Opcode::Mul,
890    Opcode::SDiv,
891    Opcode::UDiv,
892    Opcode::SRem,
893    Opcode::URem,
894    Opcode::And,
895    Opcode::Or,
896    Opcode::Xor,
897    Opcode::Shl,
898    Opcode::LShr,
899    Opcode::AShr,
900    Opcode::FAdd,
901    Opcode::FSub,
902    Opcode::FMul,
903    Opcode::FDiv,
904    Opcode::FRem,
905    Opcode::FNeg,
906    Opcode::Fma,
907    Opcode::ICmp,
908    Opcode::FCmp,
909    Opcode::Select,
910    Opcode::Trunc,
911    Opcode::SExt,
912    Opcode::ZExt,
913    Opcode::FPTrunc,
914    Opcode::FPExt,
915    Opcode::FPToSI,
916    Opcode::FPToUI,
917    Opcode::SIToFP,
918    Opcode::UIToFP,
919    Opcode::PtrToInt,
920    Opcode::IntToPtr,
921    Opcode::Bitcast,
922    Opcode::MemEntry,
923    Opcode::Alloca,
924    Opcode::Load,
925    Opcode::Store,
926    Opcode::PtrAdd,
927    Opcode::Memcpy,
928    Opcode::Memmove,
929    Opcode::Memset,
930    Opcode::AtomicLoad,
931    Opcode::AtomicStore,
932    Opcode::AtomicRmw,
933    Opcode::Cmpxchg,
934    Opcode::Fence,
935    Opcode::CapOf,
936    Opcode::CapLoad,
937    Opcode::CapStore,
938    Opcode::CapNull,
939    Opcode::CapNarrow,
940    Opcode::CapRecover,
941    Opcode::CapExtent,
942    Opcode::CapExtentBack,
943    Opcode::CheckBounds,
944    Opcode::CheckLive,
945    Opcode::CheckType,
946    Opcode::CheckInit,
947    Opcode::CheckDeriv,
948    Opcode::CheckRace,
949    Opcode::MetaBegin,
950    Opcode::MetaEnd,
951    Opcode::MetaType,
952    Opcode::MetaInit,
953    Opcode::MetaTransfer,
954    Opcode::SafeRegionBegin,
955    Opcode::SafeRegionEnd,
956    Opcode::Jump,
957    Opcode::BrIf,
958    Opcode::Switch,
959    Opcode::IndirectBr,
960    Opcode::Return,
961    Opcode::Unreachable,
962    Opcode::Call,
963    Opcode::CallIndirect,
964    Opcode::TailCall,
965    Opcode::Ctlz,
966    Opcode::Cttz,
967    Opcode::Ctpop,
968    Opcode::Bswap,
969    Opcode::Bitreverse,
970    Opcode::SAddOverflow,
971    Opcode::UAddOverflow,
972    Opcode::SSubOverflow,
973    Opcode::USubOverflow,
974    Opcode::SMulOverflow,
975    Opcode::UMulOverflow,
976    Opcode::Expect,
977    Opcode::UnreachableHint,
978    Opcode::Prefetch,
979    Opcode::FrameAddress,
980    Opcode::ReturnAddress,
981    Opcode::VaStart,
982    Opcode::VaArg,
983    Opcode::VaObject,
984    Opcode::VaEnd,
985    Opcode::VaCopy,
986    Opcode::StackSave,
987    Opcode::StackRestore,
988    Opcode::SetjmpMarker,
989    Opcode::LongjmpMarker,
990    Opcode::TargetIntrinsic,
991    Opcode::InlineAsm,
992];
993
994/// The ten integer comparisons.
995///
996/// Signedness is on the predicate rather than on the type, for the same reason it is on
997/// `sdiv` and `udiv`: the type space is halved and the operation says what it means.
998#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
999pub enum IntPred {
1000    /// Equal.
1001    Eq,
1002    /// Not equal.
1003    Ne,
1004    /// Signed less than.
1005    Slt,
1006    /// Signed less than or equal.
1007    Sle,
1008    /// Signed greater than.
1009    Sgt,
1010    /// Signed greater than or equal.
1011    Sge,
1012    /// Unsigned less than.
1013    Ult,
1014    /// Unsigned less than or equal.
1015    Ule,
1016    /// Unsigned greater than.
1017    Ugt,
1018    /// Unsigned greater than or equal.
1019    Uge,
1020}
1021
1022impl IntPred {
1023    /// The textual form.
1024    #[must_use]
1025    pub const fn name(self) -> &'static str {
1026        match self {
1027            Self::Eq => "eq",
1028            Self::Ne => "ne",
1029            Self::Slt => "slt",
1030            Self::Sle => "sle",
1031            Self::Sgt => "sgt",
1032            Self::Sge => "sge",
1033            Self::Ult => "ult",
1034            Self::Ule => "ule",
1035            Self::Ugt => "ugt",
1036            Self::Uge => "uge",
1037        }
1038    }
1039
1040    /// The predicate with that name, if there is one.
1041    #[must_use]
1042    pub fn from_name(name: &str) -> Option<Self> {
1043        Self::all().find(|pred| pred.name() == name)
1044    }
1045
1046    /// Every predicate.
1047    pub fn all() -> impl Iterator<Item = Self> {
1048        [
1049            Self::Eq,
1050            Self::Ne,
1051            Self::Slt,
1052            Self::Sle,
1053            Self::Sgt,
1054            Self::Sge,
1055            Self::Ult,
1056            Self::Ule,
1057            Self::Ugt,
1058            Self::Uge,
1059        ]
1060        .into_iter()
1061    }
1062
1063    /// The predicate that holds exactly when this one does not.
1064    #[must_use]
1065    pub const fn inverse(self) -> Self {
1066        match self {
1067            Self::Eq => Self::Ne,
1068            Self::Ne => Self::Eq,
1069            Self::Slt => Self::Sge,
1070            Self::Sge => Self::Slt,
1071            Self::Sle => Self::Sgt,
1072            Self::Sgt => Self::Sle,
1073            Self::Ult => Self::Uge,
1074            Self::Uge => Self::Ult,
1075            Self::Ule => Self::Ugt,
1076            Self::Ugt => Self::Ule,
1077        }
1078    }
1079
1080    /// The predicate that holds when the operands are given the other way round.
1081    #[must_use]
1082    pub const fn swapped(self) -> Self {
1083        match self {
1084            Self::Eq => Self::Eq,
1085            Self::Ne => Self::Ne,
1086            Self::Slt => Self::Sgt,
1087            Self::Sgt => Self::Slt,
1088            Self::Sle => Self::Sge,
1089            Self::Sge => Self::Sle,
1090            Self::Ult => Self::Ugt,
1091            Self::Ugt => Self::Ult,
1092            Self::Ule => Self::Uge,
1093            Self::Uge => Self::Ule,
1094        }
1095    }
1096
1097    /// Whether this reads its operands as signed. Equality reads them as neither.
1098    #[must_use]
1099    pub const fn is_signed(self) -> bool {
1100        matches!(self, Self::Slt | Self::Sle | Self::Sgt | Self::Sge)
1101    }
1102}
1103
1104impl fmt::Display for IntPred {
1105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1106        f.write_str(self.name())
1107    }
1108}
1109
1110/// The floating point comparisons, ordered and unordered.
1111///
1112/// An ordered predicate is false if either operand is a NaN, and an unordered one is true. C's
1113/// `<` is `olt` and C's `!=` is `une`, which is the whole of why both families are here.
1114#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
1115pub enum FloatPred {
1116    /// Always false.
1117    False,
1118    /// Ordered and equal.
1119    Oeq,
1120    /// Ordered and greater than.
1121    Ogt,
1122    /// Ordered and greater than or equal.
1123    Oge,
1124    /// Ordered and less than.
1125    Olt,
1126    /// Ordered and less than or equal.
1127    Ole,
1128    /// Ordered and not equal.
1129    One,
1130    /// Ordered, which is to say neither operand is a NaN.
1131    Ord,
1132    /// Unordered, which is to say one of them is.
1133    Uno,
1134    /// Unordered or equal.
1135    Ueq,
1136    /// Unordered or greater than.
1137    Ugt,
1138    /// Unordered or greater than or equal.
1139    Uge,
1140    /// Unordered or less than.
1141    Ult,
1142    /// Unordered or less than or equal.
1143    Ule,
1144    /// Unordered or not equal.
1145    Une,
1146    /// Always true.
1147    True,
1148}
1149
1150impl FloatPred {
1151    /// The textual form.
1152    #[must_use]
1153    pub const fn name(self) -> &'static str {
1154        match self {
1155            Self::False => "false",
1156            Self::Oeq => "oeq",
1157            Self::Ogt => "ogt",
1158            Self::Oge => "oge",
1159            Self::Olt => "olt",
1160            Self::Ole => "ole",
1161            Self::One => "one",
1162            Self::Ord => "ord",
1163            Self::Uno => "uno",
1164            Self::Ueq => "ueq",
1165            Self::Ugt => "ugt",
1166            Self::Uge => "uge",
1167            Self::Ult => "ult",
1168            Self::Ule => "ule",
1169            Self::Une => "une",
1170            Self::True => "true",
1171        }
1172    }
1173
1174    /// The predicate with that name, if there is one.
1175    #[must_use]
1176    pub fn from_name(name: &str) -> Option<Self> {
1177        Self::all().find(|pred| pred.name() == name)
1178    }
1179
1180    /// Every predicate.
1181    pub fn all() -> impl Iterator<Item = Self> {
1182        [
1183            Self::False,
1184            Self::Oeq,
1185            Self::Ogt,
1186            Self::Oge,
1187            Self::Olt,
1188            Self::Ole,
1189            Self::One,
1190            Self::Ord,
1191            Self::Uno,
1192            Self::Ueq,
1193            Self::Ugt,
1194            Self::Uge,
1195            Self::Ult,
1196            Self::Ule,
1197            Self::Une,
1198            Self::True,
1199        ]
1200        .into_iter()
1201    }
1202
1203    /// The predicate that holds exactly when this one does not.
1204    #[must_use]
1205    pub const fn inverse(self) -> Self {
1206        match self {
1207            Self::False => Self::True,
1208            Self::Oeq => Self::Une,
1209            Self::Ogt => Self::Ule,
1210            Self::Oge => Self::Ult,
1211            Self::Olt => Self::Uge,
1212            Self::Ole => Self::Ugt,
1213            Self::One => Self::Ueq,
1214            Self::Ord => Self::Uno,
1215            Self::Uno => Self::Ord,
1216            Self::Ueq => Self::One,
1217            Self::Ugt => Self::Ole,
1218            Self::Uge => Self::Olt,
1219            Self::Ult => Self::Oge,
1220            Self::Ule => Self::Ogt,
1221            Self::Une => Self::Oeq,
1222            Self::True => Self::False,
1223        }
1224    }
1225
1226    /// The predicate that holds when the operands are given the other way round.
1227    #[must_use]
1228    pub const fn swapped(self) -> Self {
1229        match self {
1230            Self::Ogt => Self::Olt,
1231            Self::Olt => Self::Ogt,
1232            Self::Oge => Self::Ole,
1233            Self::Ole => Self::Oge,
1234            Self::Ugt => Self::Ult,
1235            Self::Ult => Self::Ugt,
1236            Self::Uge => Self::Ule,
1237            Self::Ule => Self::Uge,
1238            same => same,
1239        }
1240    }
1241
1242    /// Whether this is false when either operand is a NaN.
1243    ///
1244    /// [`FloatPred::False`] and [`FloatPred::True`] are neither ordered nor unordered, since
1245    /// they do not look at their operands at all, and both answer no here.
1246    #[must_use]
1247    pub const fn is_ordered(self) -> bool {
1248        matches!(
1249            self,
1250            Self::Oeq | Self::Ogt | Self::Oge | Self::Olt | Self::Ole | Self::One | Self::Ord
1251        )
1252    }
1253}
1254
1255impl fmt::Display for FloatPred {
1256    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1257        f.write_str(self.name())
1258    }
1259}
1260
1261#[cfg(test)]
1262mod tests {
1263    use super::*;
1264
1265    #[test]
1266    fn every_opcode_is_in_the_table() {
1267        // `InlineAsm` is the last variant, so its discriminant plus one is how many there are.
1268        // A new opcode declared after it moves this number, and a new opcode declared before
1269        // it and not added to `ALL` moves the length, so either mistake fails here.
1270        assert_eq!(ALL.len(), Opcode::InlineAsm as usize + 1);
1271        for (position, &op) in ALL.iter().enumerate() {
1272            assert_eq!(op as usize, position, "{op} is out of order in ALL");
1273        }
1274    }
1275
1276    #[test]
1277    fn every_opcode_name_is_one_word_the_reader_can_take() {
1278        // The textual form keeps the dot for the type suffix and the flags, so an opcode with a
1279        // dot in it reads back as a shorter opcode with a suffix that is not a type. The safety
1280        // instructions are spelled `cap_of` and not `cap.of` for this reason, and the
1281        // specification says so at `spec/safe-memory/06-instrumentation.md` section 6.2.2.
1282        for opcode in Opcode::all() {
1283            let name = opcode.name();
1284            assert!(!name.is_empty(), "an opcode with no name");
1285            assert!(
1286                name.bytes().all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'_'),
1287                "{name} is not one word"
1288            );
1289        }
1290    }
1291
1292    #[test]
1293    fn every_opcode_has_its_own_name_and_finds_it_again() {
1294        let mut names: Vec<&str> = Opcode::all().map(Opcode::name).collect();
1295        let total = names.len();
1296        names.sort_unstable();
1297        names.dedup();
1298        assert_eq!(names.len(), total, "two opcodes share a name");
1299        for op in Opcode::all() {
1300            assert_eq!(Opcode::from_name(op.name()), Some(op));
1301        }
1302        assert_eq!(Opcode::from_name("phi"), None);
1303        assert_eq!(Opcode::from_name("getelementptr"), None);
1304        assert_eq!(Opcode::from_name(""), None);
1305    }
1306
1307    #[test]
1308    fn the_terminators_are_the_ones_control_leaves_by() {
1309        let terminators: Vec<&str> =
1310            Opcode::all().filter(|op| op.is_terminator()).map(Opcode::name).collect();
1311        assert_eq!(
1312            terminators,
1313            ["jump", "br_if", "switch", "indirect_br", "return", "unreachable", "tail_call"]
1314        );
1315    }
1316
1317    #[test]
1318    fn a_terminator_produces_nothing() {
1319        for op in Opcode::all().filter(|op| op.is_terminator()) {
1320            assert_eq!(op.results(), Some(0), "{op}");
1321        }
1322    }
1323
1324    #[test]
1325    fn the_pair_producing_opcodes_are_the_ones_with_a_flag_beside_the_value() {
1326        let pairs: Vec<&str> =
1327            Opcode::all().filter(|op| op.results() == Some(2)).map(Opcode::name).collect();
1328        assert_eq!(
1329            pairs,
1330            [
1331                "cmpxchg",
1332                "sadd_overflow",
1333                "uadd_overflow",
1334                "ssub_overflow",
1335                "usub_overflow",
1336                "smul_overflow",
1337                "umul_overflow"
1338            ]
1339        );
1340    }
1341
1342    #[test]
1343    fn the_capability_instructions_are_the_ones_that_make_a_capability() {
1344        let makers: Vec<Opcode> = Opcode::all().filter(|op| op.makes_capability()).collect();
1345        assert_eq!(
1346            makers,
1347            vec![
1348                Opcode::CapOf,
1349                Opcode::CapLoad,
1350                Opcode::CapNull,
1351                Opcode::CapNarrow,
1352                Opcode::CapRecover
1353            ]
1354        );
1355        // The other three read a capability rather than making one. `cap_store` writes it out and
1356        // produces nothing at all, and the two extent queries answer with a number.
1357        assert!(!Opcode::CapStore.makes_capability());
1358        assert_eq!(Opcode::CapStore.results(), Some(0));
1359        assert!(!Opcode::CapExtent.makes_capability());
1360        assert_eq!(Opcode::CapExtent.results(), Some(1));
1361        assert!(!Opcode::CapExtentBack.makes_capability());
1362        assert_eq!(Opcode::CapExtentBack.results(), Some(1));
1363        for opcode in makers {
1364            assert_eq!(opcode.results(), Some(1), "{}", opcode.name());
1365        }
1366    }
1367
1368    #[test]
1369    fn a_check_reads_the_planes_and_writes_nothing() {
1370        let checks = [
1371            Opcode::CheckBounds,
1372            Opcode::CheckLive,
1373            Opcode::CheckType,
1374            Opcode::CheckInit,
1375            Opcode::CheckDeriv,
1376            Opcode::CheckRace,
1377        ];
1378        for opcode in checks {
1379            let name = opcode.name();
1380            // It traps, so it stays where it was put and nothing deletes it for having no
1381            // result. It reads a plane, so it takes a memory operand. It writes nothing, so
1382            // the access after it reads the version the check was given.
1383            assert!(opcode.has_effects(), "{name}");
1384            assert!(opcode.touches_memory(), "{name}");
1385            assert!(!opcode.writes_memory(), "{name}");
1386            assert_eq!(opcode.results(), Some(0), "{name}");
1387        }
1388    }
1389
1390    #[test]
1391    fn the_capability_instructions_that_touch_memory_are_the_five_that_have_to() {
1392        // `cap_load` and `cap_store` are an access to the slot beside a pointer, and `cap_recover`
1393        // and the two extent queries read the planes. The other three are arithmetic on a
1394        // provenance the program already had, so the optimizer may treat them as it treats
1395        // `ptr_add`.
1396        assert!(!Opcode::CapOf.has_effects());
1397        assert!(!Opcode::CapNull.has_effects());
1398        assert!(!Opcode::CapNarrow.has_effects());
1399        assert!(Opcode::CapLoad.touches_memory() && !Opcode::CapLoad.writes_memory());
1400        assert!(Opcode::CapRecover.touches_memory() && !Opcode::CapRecover.writes_memory());
1401        assert!(Opcode::CapExtent.touches_memory() && !Opcode::CapExtent.writes_memory());
1402        assert!(Opcode::CapExtentBack.touches_memory() && !Opcode::CapExtentBack.writes_memory());
1403        assert!(Opcode::CapStore.writes_memory());
1404    }
1405
1406    #[test]
1407    fn memory_has_effects_and_arithmetic_does_not() {
1408        for op in [Opcode::Load, Opcode::Store, Opcode::Call, Opcode::Alloca, Opcode::Fence] {
1409            assert!(op.has_effects(), "{op}");
1410        }
1411        for op in [Opcode::Add, Opcode::FDiv, Opcode::ICmp, Opcode::PtrAdd, Opcode::IConst] {
1412            assert!(!op.has_effects(), "{op}");
1413        }
1414    }
1415
1416    #[test]
1417    fn commuting_is_only_claimed_where_it_holds() {
1418        assert!(Opcode::Add.is_commutative());
1419        assert!(Opcode::FAdd.is_commutative());
1420        assert!(!Opcode::Sub.is_commutative());
1421        assert!(!Opcode::FDiv.is_commutative());
1422        assert!(!Opcode::Shl.is_commutative());
1423    }
1424
1425    #[test]
1426    fn an_integer_predicate_inverts_and_swaps_back_to_itself() {
1427        for pred in IntPred::all() {
1428            assert_eq!(pred.inverse().inverse(), pred);
1429            assert_eq!(pred.swapped().swapped(), pred);
1430            assert_eq!(IntPred::from_name(pred.name()), Some(pred));
1431        }
1432        assert_eq!(IntPred::Slt.inverse(), IntPred::Sge);
1433        assert_eq!(IntPred::Slt.swapped(), IntPred::Sgt);
1434        assert_eq!(IntPred::from_name("lt"), None);
1435    }
1436
1437    #[test]
1438    fn a_floating_predicate_inverts_across_the_ordered_line() {
1439        for pred in FloatPred::all() {
1440            assert_eq!(pred.inverse().inverse(), pred);
1441            assert_eq!(pred.swapped().swapped(), pred);
1442            assert_eq!(FloatPred::from_name(pred.name()), Some(pred));
1443        }
1444        // Inverting has to cross the line, because the negation of an ordered comparison is
1445        // true when an operand is a NaN. This is where `!(a < b)` stops being `a >= b`. The
1446        // two constants are outside it: neither of them looks at its operands.
1447        for pred in FloatPred::all().filter(|p| !matches!(p, FloatPred::False | FloatPred::True)) {
1448            assert_ne!(pred.is_ordered(), pred.inverse().is_ordered(), "{pred}");
1449        }
1450        assert_eq!(FloatPred::Olt.inverse(), FloatPred::Uge);
1451        assert_eq!(FloatPred::Olt.swapped(), FloatPred::Ogt);
1452    }
1453
1454    #[test]
1455    fn swapping_a_predicate_keeps_it_ordered_or_unordered() {
1456        for pred in FloatPred::all() {
1457            assert_eq!(pred.is_ordered(), pred.swapped().is_ordered(), "{pred}");
1458        }
1459        for pred in IntPred::all() {
1460            assert_eq!(pred.is_signed(), pred.swapped().is_signed(), "{pred}");
1461        }
1462    }
1463
1464    #[test]
1465    fn no_two_predicates_share_a_name_within_their_family() {
1466        for names in [
1467            IntPred::all().map(IntPred::name).collect::<Vec<_>>(),
1468            FloatPred::all().map(FloatPred::name).collect::<Vec<_>>(),
1469        ] {
1470            let total = names.len();
1471            let mut names = names;
1472            names.sort_unstable();
1473            names.dedup();
1474            assert_eq!(names.len(), total);
1475        }
1476    }
1477}