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}