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