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