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 /// A stack slot. In the entry block, or marked dynamic for a variable length array.
123 Alloca,
124 /// A read.
125 Load,
126 /// A write, producing no value.
127 Store,
128 /// Address arithmetic: an address and a byte offset.
129 PtrAdd,
130 /// A copy of a known size between addresses that do not overlap.
131 Memcpy,
132 /// A copy of a known size between addresses that may overlap.
133 Memmove,
134 /// A fill of a known size with one byte.
135 Memset,
136 /// An atomic read.
137 AtomicLoad,
138 /// An atomic write.
139 AtomicStore,
140 /// An atomic read-modify-write, carrying which operation in [`RmwOp`](crate::RmwOp).
141 AtomicRmw,
142 /// An atomic compare and exchange, producing the old value and whether it succeeded.
143 Cmpxchg,
144 /// A memory barrier.
145 Fence,
146
147 // Control. Every one of these is a terminator.
148 /// An unconditional branch, `jump block1(%a, %b)`.
149 Jump,
150 /// A two-way branch on an `i1`.
151 BrIf,
152 /// A multi-way branch on an integer, with a default.
153 Switch,
154 /// A branch to an address, `indirect_br %0, block1, block2`.
155 ///
156 /// The targets are every block control can arrive at, which is what makes the edges of a
157 /// computed `goto` ordinary edges: nothing else in the compiler has to know that the
158 /// address decides which one it is. A target that is not listed is a branch that does not
159 /// happen, so a frontend that leaves one out has made a promise on the program's behalf.
160 IndirectBr,
161 /// A return, with the values the signature says.
162 Return,
163 /// A place control cannot reach, which the frontend emits after a `noreturn` call.
164 Unreachable,
165
166 // Calls.
167 /// A call to a named function.
168 Call,
169 /// A call through an address, carrying the signature it is called with.
170 CallIndirect,
171 /// A call in tail position that reuses the frame, which is a terminator.
172 TailCall,
173
174 // Intrinsics, which is the closed part. The open part is `TargetIntrinsic`.
175 /// Count leading zeroes.
176 Ctlz,
177 /// Count trailing zeroes.
178 Cttz,
179 /// Count set bits.
180 Ctpop,
181 /// Reverse the bytes.
182 Bswap,
183 /// Reverse the bits.
184 Bitreverse,
185 /// Signed addition, producing the result and whether it overflowed.
186 SAddOverflow,
187 /// Unsigned addition, producing the result and whether it overflowed.
188 UAddOverflow,
189 /// Signed subtraction, producing the result and whether it overflowed.
190 SSubOverflow,
191 /// Unsigned subtraction, producing the result and whether it overflowed.
192 USubOverflow,
193 /// Signed multiplication, producing the result and whether it overflowed.
194 SMulOverflow,
195 /// Unsigned multiplication, producing the result and whether it overflowed.
196 UMulOverflow,
197 /// `__builtin_expect`, which is the value with a hint attached.
198 Expect,
199 /// `__builtin_unreachable` as a hint on a path, distinct from the terminator.
200 UnreachableHint,
201 /// `__builtin_prefetch`.
202 Prefetch,
203 /// `__builtin_frame_address`.
204 FrameAddress,
205 /// `__builtin_return_address`.
206 ReturnAddress,
207 /// The start of a variable argument list.
208 VaStart,
209 /// One argument off a variable argument list, which moves the list on as it reads it. Two
210 /// of these on one list are two arguments and never one argument read twice, so whatever
211 /// decides which instructions may be folded together has to leave these alone.
212 VaArg,
213 /// One argument off a variable argument list, when that argument is an object rather than a
214 /// value, which is what a `struct` or a `union` read out of one is.
215 ///
216 /// It answers the address of the object rather than the object, because an aggregate is not
217 /// a value and there is nothing for one result to be. Where the object arrives in registers
218 /// there is no address until something makes one, so what this asks of a target is a place
219 /// to put the registers and the address of that place, which is the copy every psABI's own
220 /// description of the algorithm makes. It moves the list on for the reason [`Opcode::VaArg`]
221 /// does.
222 VaObject,
223 /// The end of a variable argument list.
224 VaEnd,
225 /// A copy of a variable argument list.
226 VaCopy,
227 /// The stack pointer, saved before a variable length array.
228 StackSave,
229 /// The stack pointer, restored after one.
230 StackRestore,
231 /// The marker a `setjmp` leaves, which pins everything live across it.
232 SetjmpMarker,
233 /// The marker a `longjmp` leaves.
234 LongjmpMarker,
235 /// A target-specific intrinsic, named rather than enumerated, for the vector builtins.
236 TargetIntrinsic,
237
238 /// Inline assembly. A terminator when it has labels, which is `asm goto`.
239 InlineAsm,
240}
241
242impl Opcode {
243 /// The textual form, which is also what the parser reads.
244 #[must_use]
245 pub const fn name(self) -> &'static str {
246 match self {
247 Self::IConst => "iconst",
248 Self::FConst => "fconst",
249 Self::Splat => "splat",
250 Self::GlobalAddr => "global_addr",
251 Self::BlockAddr => "block_addr",
252 Self::Add => "add",
253 Self::Sub => "sub",
254 Self::Mul => "mul",
255 Self::SDiv => "sdiv",
256 Self::UDiv => "udiv",
257 Self::SRem => "srem",
258 Self::URem => "urem",
259 Self::And => "and",
260 Self::Or => "or",
261 Self::Xor => "xor",
262 Self::Shl => "shl",
263 Self::LShr => "lshr",
264 Self::AShr => "ashr",
265 Self::FAdd => "fadd",
266 Self::FSub => "fsub",
267 Self::FMul => "fmul",
268 Self::FDiv => "fdiv",
269 Self::FRem => "frem",
270 Self::FNeg => "fneg",
271 Self::Fma => "fma",
272 Self::ICmp => "icmp",
273 Self::FCmp => "fcmp",
274 Self::Trunc => "trunc",
275 Self::SExt => "sext",
276 Self::ZExt => "zext",
277 Self::FPTrunc => "fptrunc",
278 Self::FPExt => "fpext",
279 Self::FPToSI => "fptosi",
280 Self::FPToUI => "fptoui",
281 Self::SIToFP => "sitofp",
282 Self::UIToFP => "uitofp",
283 Self::PtrToInt => "ptrtoint",
284 Self::IntToPtr => "inttoptr",
285 Self::Bitcast => "bitcast",
286 Self::Alloca => "alloca",
287 Self::Load => "load",
288 Self::Store => "store",
289 Self::PtrAdd => "ptr_add",
290 Self::Memcpy => "memcpy",
291 Self::Memmove => "memmove",
292 Self::Memset => "memset",
293 Self::AtomicLoad => "atomic_load",
294 Self::AtomicStore => "atomic_store",
295 Self::AtomicRmw => "atomic_rmw",
296 Self::Cmpxchg => "cmpxchg",
297 Self::Fence => "fence",
298 Self::Jump => "jump",
299 Self::BrIf => "br_if",
300 Self::Switch => "switch",
301 Self::IndirectBr => "indirect_br",
302 Self::Return => "return",
303 Self::Unreachable => "unreachable",
304 Self::Call => "call",
305 Self::CallIndirect => "call_indirect",
306 Self::TailCall => "tail_call",
307 Self::Ctlz => "ctlz",
308 Self::Cttz => "cttz",
309 Self::Ctpop => "ctpop",
310 Self::Bswap => "bswap",
311 Self::Bitreverse => "bitreverse",
312 Self::SAddOverflow => "sadd_overflow",
313 Self::UAddOverflow => "uadd_overflow",
314 Self::SSubOverflow => "ssub_overflow",
315 Self::USubOverflow => "usub_overflow",
316 Self::SMulOverflow => "smul_overflow",
317 Self::UMulOverflow => "umul_overflow",
318 Self::Expect => "expect",
319 Self::UnreachableHint => "unreachable_hint",
320 Self::Prefetch => "prefetch",
321 Self::FrameAddress => "frame_address",
322 Self::ReturnAddress => "return_address",
323 Self::VaStart => "va_start",
324 Self::VaArg => "va_arg",
325 Self::VaObject => "va_object",
326 Self::VaEnd => "va_end",
327 Self::VaCopy => "va_copy",
328 Self::StackSave => "stacksave",
329 Self::StackRestore => "stackrestore",
330 Self::SetjmpMarker => "setjmp_marker",
331 Self::LongjmpMarker => "longjmp_marker",
332 Self::TargetIntrinsic => "target_intrinsic",
333 Self::InlineAsm => "inline_asm",
334 }
335 }
336
337 /// Every opcode, in the order they are declared.
338 ///
339 /// The parser walks this rather than holding a second table, because a second table is a
340 /// table that can disagree with the first one.
341 pub fn all() -> impl Iterator<Item = Self> {
342 ALL.iter().copied()
343 }
344
345 /// The opcode with that name, if there is one.
346 #[must_use]
347 pub fn from_name(name: &str) -> Option<Self> {
348 ALL.iter().copied().find(|op| op.name() == name)
349 }
350
351 /// Whether this ends a block.
352 ///
353 /// [`Opcode::InlineAsm`] is not here and is the one instruction whose answer depends on
354 /// the instruction rather than on the opcode: `asm goto` has successors and everything
355 /// else does not. Ask the instruction, not the opcode.
356 #[must_use]
357 pub const fn is_terminator(self) -> bool {
358 matches!(
359 self,
360 Self::Jump
361 | Self::BrIf
362 | Self::Switch
363 | Self::IndirectBr
364 | Self::Return
365 | Self::Unreachable
366 | Self::TailCall
367 )
368 }
369
370 /// Whether the operands can be swapped without changing the result.
371 ///
372 /// The floating point cases are commutative even under the strictest rounding, because
373 /// swapping the operands of an addition does not change which of them is a NaN, and the
374 /// sign of a NaN result is not something we promise anything about either way.
375 #[must_use]
376 pub const fn is_commutative(self) -> bool {
377 matches!(
378 self,
379 Self::Add
380 | Self::Mul
381 | Self::And
382 | Self::Or
383 | Self::Xor
384 | Self::FAdd
385 | Self::FMul
386 | Self::SAddOverflow
387 | Self::UAddOverflow
388 | Self::SMulOverflow
389 | Self::UMulOverflow
390 )
391 }
392
393 /// Whether this reads or writes memory, or has an effect the optimizer has to preserve.
394 ///
395 /// An instruction that answers no can be deleted when nothing uses its result, moved
396 /// across a call, and merged with another one computing the same thing. Everything else
397 /// has to be argued about individually, so the conservative answer is the true one here
398 /// and the list of exceptions is the part that is checked.
399 #[must_use]
400 pub const fn has_effects(self) -> bool {
401 !matches!(
402 self,
403 Self::IConst
404 | Self::FConst
405 | Self::Splat
406 | Self::GlobalAddr
407 | Self::BlockAddr
408 | Self::Add
409 | Self::Sub
410 | Self::Mul
411 | Self::SDiv
412 | Self::UDiv
413 | Self::SRem
414 | Self::URem
415 | Self::And
416 | Self::Or
417 | Self::Xor
418 | Self::Shl
419 | Self::LShr
420 | Self::AShr
421 | Self::FAdd
422 | Self::FSub
423 | Self::FMul
424 | Self::FDiv
425 | Self::FRem
426 | Self::FNeg
427 | Self::Fma
428 | Self::ICmp
429 | Self::FCmp
430 | Self::Trunc
431 | Self::SExt
432 | Self::ZExt
433 | Self::FPTrunc
434 | Self::FPExt
435 | Self::FPToSI
436 | Self::FPToUI
437 | Self::SIToFP
438 | Self::UIToFP
439 | Self::PtrToInt
440 | Self::IntToPtr
441 | Self::Bitcast
442 | Self::PtrAdd
443 | Self::Ctlz
444 | Self::Cttz
445 | Self::Ctpop
446 | Self::Bswap
447 | Self::Bitreverse
448 | Self::SAddOverflow
449 | Self::UAddOverflow
450 | Self::SSubOverflow
451 | Self::USubOverflow
452 | Self::SMulOverflow
453 | Self::UMulOverflow
454 | Self::Expect
455 | Self::FrameAddress
456 | Self::ReturnAddress
457 )
458 }
459
460 /// How many values this produces, for the opcodes where the count is fixed.
461 ///
462 /// `None` means the count comes from somewhere else: a call takes it from its signature,
463 /// and inline assembly takes it from its output constraints. A tail call is not one of
464 /// them, because whatever it returns goes straight out of the function and there is no
465 /// instruction after it to use anything.
466 #[must_use]
467 pub const fn results(self) -> Option<u8> {
468 match self {
469 Self::Call | Self::CallIndirect | Self::InlineAsm => None,
470 Self::Cmpxchg
471 | Self::SAddOverflow
472 | Self::UAddOverflow
473 | Self::SSubOverflow
474 | Self::USubOverflow
475 | Self::SMulOverflow
476 | Self::UMulOverflow => Some(2),
477 Self::Store
478 | Self::Memcpy
479 | Self::Memmove
480 | Self::Memset
481 | Self::AtomicStore
482 | Self::Fence
483 | Self::Prefetch
484 | Self::VaStart
485 | Self::VaEnd
486 | Self::VaCopy
487 | Self::StackRestore
488 | Self::UnreachableHint
489 | Self::SetjmpMarker
490 | Self::LongjmpMarker => Some(0),
491 _ if self.is_terminator() => Some(0),
492 _ => Some(1),
493 }
494 }
495
496 /// Which payload an instruction with this opcode carries.
497 ///
498 /// The printer reads the payload it finds and does not need this. The parser has only the
499 /// opcode when it reaches the operands, so this is where the two of them agree on what
500 /// comes after them. An instruction carrying a payload of some other kind prints as text
501 /// the parser cannot read back, which is why the verifier checks it against
502 /// [`Extra::kind`](crate::Extra::kind) rather than leaving it to be found later.
503 #[must_use]
504 pub const fn extra_kind(self) -> ExtraKind {
505 match self {
506 Self::IConst | Self::FConst | Self::Splat => ExtraKind::Imm,
507 Self::GlobalAddr | Self::TargetIntrinsic => ExtraKind::Symbol,
508 Self::ICmp => ExtraKind::IntPred,
509 Self::FCmp => ExtraKind::FloatPred,
510 Self::Alloca
511 | Self::Load
512 | Self::Store
513 | Self::Memcpy
514 | Self::Memmove
515 | Self::Memset
516 | Self::AtomicLoad
517 | Self::AtomicStore
518 | Self::Cmpxchg
519 | Self::VaObject => ExtraKind::Mem,
520 Self::AtomicRmw => ExtraKind::Rmw,
521 Self::Fence => ExtraKind::Order,
522 Self::Jump | Self::BrIf | Self::BlockAddr | Self::IndirectBr => ExtraKind::Targets,
523 Self::Switch => ExtraKind::Switch,
524 Self::Call | Self::CallIndirect | Self::TailCall => ExtraKind::Call,
525 Self::InlineAsm => ExtraKind::Asm,
526 _ => ExtraKind::None,
527 }
528 }
529}
530
531/// Which of [`Extra`](crate::Extra)'s shapes an instruction carries.
532///
533/// The same list of names, without any of the payloads, so that a question about an opcode can
534/// be answered without an instruction to look at.
535#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
536pub enum ExtraKind {
537 /// Nothing.
538 None,
539 /// A constant.
540 Imm,
541 /// A name.
542 Symbol,
543 /// An integer comparison predicate.
544 IntPred,
545 /// A floating point comparison predicate.
546 FloatPred,
547 /// An access.
548 Mem,
549 /// An atomic read-modify-write.
550 Rmw,
551 /// A barrier's ordering.
552 Order,
553 /// Branch targets.
554 Targets,
555 /// A call.
556 Call,
557 /// A `switch`.
558 Switch,
559 /// Inline assembly.
560 Asm,
561}
562
563impl ExtraKind {
564 /// What it is, in words, for a message that names two of them and has to read as English.
565 #[must_use]
566 pub const fn name(self) -> &'static str {
567 match self {
568 Self::None => "nothing",
569 Self::Imm => "a constant",
570 Self::Symbol => "a name",
571 Self::IntPred => "an integer comparison",
572 Self::FloatPred => "a floating point comparison",
573 Self::Mem => "an access",
574 Self::Rmw => "a read-modify-write",
575 Self::Order => "an ordering",
576 Self::Targets => "branch targets",
577 Self::Call => "a call",
578 Self::Switch => "a switch",
579 Self::Asm => "inline assembly",
580 }
581 }
582}
583
584impl fmt::Display for Opcode {
585 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
586 f.write_str(self.name())
587 }
588}
589
590/// Every opcode, which is what [`Opcode::all`] hands out.
591///
592/// This is written out rather than derived, and the test below is what keeps it complete: it
593/// checks the count against [`Opcode::InlineAsm`], the last variant, so a new opcode that is
594/// not added here fails the build rather than going quietly missing from the parser.
595static ALL: &[Opcode] = &[
596 Opcode::IConst,
597 Opcode::FConst,
598 Opcode::Splat,
599 Opcode::GlobalAddr,
600 Opcode::BlockAddr,
601 Opcode::Add,
602 Opcode::Sub,
603 Opcode::Mul,
604 Opcode::SDiv,
605 Opcode::UDiv,
606 Opcode::SRem,
607 Opcode::URem,
608 Opcode::And,
609 Opcode::Or,
610 Opcode::Xor,
611 Opcode::Shl,
612 Opcode::LShr,
613 Opcode::AShr,
614 Opcode::FAdd,
615 Opcode::FSub,
616 Opcode::FMul,
617 Opcode::FDiv,
618 Opcode::FRem,
619 Opcode::FNeg,
620 Opcode::Fma,
621 Opcode::ICmp,
622 Opcode::FCmp,
623 Opcode::Trunc,
624 Opcode::SExt,
625 Opcode::ZExt,
626 Opcode::FPTrunc,
627 Opcode::FPExt,
628 Opcode::FPToSI,
629 Opcode::FPToUI,
630 Opcode::SIToFP,
631 Opcode::UIToFP,
632 Opcode::PtrToInt,
633 Opcode::IntToPtr,
634 Opcode::Bitcast,
635 Opcode::Alloca,
636 Opcode::Load,
637 Opcode::Store,
638 Opcode::PtrAdd,
639 Opcode::Memcpy,
640 Opcode::Memmove,
641 Opcode::Memset,
642 Opcode::AtomicLoad,
643 Opcode::AtomicStore,
644 Opcode::AtomicRmw,
645 Opcode::Cmpxchg,
646 Opcode::Fence,
647 Opcode::Jump,
648 Opcode::BrIf,
649 Opcode::Switch,
650 Opcode::IndirectBr,
651 Opcode::Return,
652 Opcode::Unreachable,
653 Opcode::Call,
654 Opcode::CallIndirect,
655 Opcode::TailCall,
656 Opcode::Ctlz,
657 Opcode::Cttz,
658 Opcode::Ctpop,
659 Opcode::Bswap,
660 Opcode::Bitreverse,
661 Opcode::SAddOverflow,
662 Opcode::UAddOverflow,
663 Opcode::SSubOverflow,
664 Opcode::USubOverflow,
665 Opcode::SMulOverflow,
666 Opcode::UMulOverflow,
667 Opcode::Expect,
668 Opcode::UnreachableHint,
669 Opcode::Prefetch,
670 Opcode::FrameAddress,
671 Opcode::ReturnAddress,
672 Opcode::VaStart,
673 Opcode::VaArg,
674 Opcode::VaObject,
675 Opcode::VaEnd,
676 Opcode::VaCopy,
677 Opcode::StackSave,
678 Opcode::StackRestore,
679 Opcode::SetjmpMarker,
680 Opcode::LongjmpMarker,
681 Opcode::TargetIntrinsic,
682 Opcode::InlineAsm,
683];
684
685/// The ten integer comparisons.
686///
687/// Signedness is on the predicate rather than on the type, for the same reason it is on
688/// `sdiv` and `udiv`: the type space is halved and the operation says what it means.
689#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
690pub enum IntPred {
691 /// Equal.
692 Eq,
693 /// Not equal.
694 Ne,
695 /// Signed less than.
696 Slt,
697 /// Signed less than or equal.
698 Sle,
699 /// Signed greater than.
700 Sgt,
701 /// Signed greater than or equal.
702 Sge,
703 /// Unsigned less than.
704 Ult,
705 /// Unsigned less than or equal.
706 Ule,
707 /// Unsigned greater than.
708 Ugt,
709 /// Unsigned greater than or equal.
710 Uge,
711}
712
713impl IntPred {
714 /// The textual form.
715 #[must_use]
716 pub const fn name(self) -> &'static str {
717 match self {
718 Self::Eq => "eq",
719 Self::Ne => "ne",
720 Self::Slt => "slt",
721 Self::Sle => "sle",
722 Self::Sgt => "sgt",
723 Self::Sge => "sge",
724 Self::Ult => "ult",
725 Self::Ule => "ule",
726 Self::Ugt => "ugt",
727 Self::Uge => "uge",
728 }
729 }
730
731 /// The predicate with that name, if there is one.
732 #[must_use]
733 pub fn from_name(name: &str) -> Option<Self> {
734 Self::all().find(|pred| pred.name() == name)
735 }
736
737 /// Every predicate.
738 pub fn all() -> impl Iterator<Item = Self> {
739 [
740 Self::Eq,
741 Self::Ne,
742 Self::Slt,
743 Self::Sle,
744 Self::Sgt,
745 Self::Sge,
746 Self::Ult,
747 Self::Ule,
748 Self::Ugt,
749 Self::Uge,
750 ]
751 .into_iter()
752 }
753
754 /// The predicate that holds exactly when this one does not.
755 #[must_use]
756 pub const fn inverse(self) -> Self {
757 match self {
758 Self::Eq => Self::Ne,
759 Self::Ne => Self::Eq,
760 Self::Slt => Self::Sge,
761 Self::Sge => Self::Slt,
762 Self::Sle => Self::Sgt,
763 Self::Sgt => Self::Sle,
764 Self::Ult => Self::Uge,
765 Self::Uge => Self::Ult,
766 Self::Ule => Self::Ugt,
767 Self::Ugt => Self::Ule,
768 }
769 }
770
771 /// The predicate that holds when the operands are given the other way round.
772 #[must_use]
773 pub const fn swapped(self) -> Self {
774 match self {
775 Self::Eq => Self::Eq,
776 Self::Ne => Self::Ne,
777 Self::Slt => Self::Sgt,
778 Self::Sgt => Self::Slt,
779 Self::Sle => Self::Sge,
780 Self::Sge => Self::Sle,
781 Self::Ult => Self::Ugt,
782 Self::Ugt => Self::Ult,
783 Self::Ule => Self::Uge,
784 Self::Uge => Self::Ule,
785 }
786 }
787
788 /// Whether this reads its operands as signed. Equality reads them as neither.
789 #[must_use]
790 pub const fn is_signed(self) -> bool {
791 matches!(self, Self::Slt | Self::Sle | Self::Sgt | Self::Sge)
792 }
793}
794
795impl fmt::Display for IntPred {
796 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
797 f.write_str(self.name())
798 }
799}
800
801/// The floating point comparisons, ordered and unordered.
802///
803/// An ordered predicate is false if either operand is a NaN, and an unordered one is true. C's
804/// `<` is `olt` and C's `!=` is `une`, which is the whole of why both families are here.
805#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
806pub enum FloatPred {
807 /// Always false.
808 False,
809 /// Ordered and equal.
810 Oeq,
811 /// Ordered and greater than.
812 Ogt,
813 /// Ordered and greater than or equal.
814 Oge,
815 /// Ordered and less than.
816 Olt,
817 /// Ordered and less than or equal.
818 Ole,
819 /// Ordered and not equal.
820 One,
821 /// Ordered, which is to say neither operand is a NaN.
822 Ord,
823 /// Unordered, which is to say one of them is.
824 Uno,
825 /// Unordered or equal.
826 Ueq,
827 /// Unordered or greater than.
828 Ugt,
829 /// Unordered or greater than or equal.
830 Uge,
831 /// Unordered or less than.
832 Ult,
833 /// Unordered or less than or equal.
834 Ule,
835 /// Unordered or not equal.
836 Une,
837 /// Always true.
838 True,
839}
840
841impl FloatPred {
842 /// The textual form.
843 #[must_use]
844 pub const fn name(self) -> &'static str {
845 match self {
846 Self::False => "false",
847 Self::Oeq => "oeq",
848 Self::Ogt => "ogt",
849 Self::Oge => "oge",
850 Self::Olt => "olt",
851 Self::Ole => "ole",
852 Self::One => "one",
853 Self::Ord => "ord",
854 Self::Uno => "uno",
855 Self::Ueq => "ueq",
856 Self::Ugt => "ugt",
857 Self::Uge => "uge",
858 Self::Ult => "ult",
859 Self::Ule => "ule",
860 Self::Une => "une",
861 Self::True => "true",
862 }
863 }
864
865 /// The predicate with that name, if there is one.
866 #[must_use]
867 pub fn from_name(name: &str) -> Option<Self> {
868 Self::all().find(|pred| pred.name() == name)
869 }
870
871 /// Every predicate.
872 pub fn all() -> impl Iterator<Item = Self> {
873 [
874 Self::False,
875 Self::Oeq,
876 Self::Ogt,
877 Self::Oge,
878 Self::Olt,
879 Self::Ole,
880 Self::One,
881 Self::Ord,
882 Self::Uno,
883 Self::Ueq,
884 Self::Ugt,
885 Self::Uge,
886 Self::Ult,
887 Self::Ule,
888 Self::Une,
889 Self::True,
890 ]
891 .into_iter()
892 }
893
894 /// The predicate that holds exactly when this one does not.
895 #[must_use]
896 pub const fn inverse(self) -> Self {
897 match self {
898 Self::False => Self::True,
899 Self::Oeq => Self::Une,
900 Self::Ogt => Self::Ule,
901 Self::Oge => Self::Ult,
902 Self::Olt => Self::Uge,
903 Self::Ole => Self::Ugt,
904 Self::One => Self::Ueq,
905 Self::Ord => Self::Uno,
906 Self::Uno => Self::Ord,
907 Self::Ueq => Self::One,
908 Self::Ugt => Self::Ole,
909 Self::Uge => Self::Olt,
910 Self::Ult => Self::Oge,
911 Self::Ule => Self::Ogt,
912 Self::Une => Self::Oeq,
913 Self::True => Self::False,
914 }
915 }
916
917 /// The predicate that holds when the operands are given the other way round.
918 #[must_use]
919 pub const fn swapped(self) -> Self {
920 match self {
921 Self::Ogt => Self::Olt,
922 Self::Olt => Self::Ogt,
923 Self::Oge => Self::Ole,
924 Self::Ole => Self::Oge,
925 Self::Ugt => Self::Ult,
926 Self::Ult => Self::Ugt,
927 Self::Uge => Self::Ule,
928 Self::Ule => Self::Uge,
929 same => same,
930 }
931 }
932
933 /// Whether this is false when either operand is a NaN.
934 ///
935 /// [`FloatPred::False`] and [`FloatPred::True`] are neither ordered nor unordered, since
936 /// they do not look at their operands at all, and both answer no here.
937 #[must_use]
938 pub const fn is_ordered(self) -> bool {
939 matches!(
940 self,
941 Self::Oeq | Self::Ogt | Self::Oge | Self::Olt | Self::Ole | Self::One | Self::Ord
942 )
943 }
944}
945
946impl fmt::Display for FloatPred {
947 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
948 f.write_str(self.name())
949 }
950}
951
952#[cfg(test)]
953mod tests {
954 use super::*;
955
956 #[test]
957 fn every_opcode_is_in_the_table() {
958 // `InlineAsm` is the last variant, so its discriminant plus one is how many there are.
959 // A new opcode declared after it moves this number, and a new opcode declared before
960 // it and not added to `ALL` moves the length, so either mistake fails here.
961 assert_eq!(ALL.len(), Opcode::InlineAsm as usize + 1);
962 for (position, &op) in ALL.iter().enumerate() {
963 assert_eq!(op as usize, position, "{op} is out of order in ALL");
964 }
965 }
966
967 #[test]
968 fn every_opcode_has_its_own_name_and_finds_it_again() {
969 let mut names: Vec<&str> = Opcode::all().map(Opcode::name).collect();
970 let total = names.len();
971 names.sort_unstable();
972 names.dedup();
973 assert_eq!(names.len(), total, "two opcodes share a name");
974 for op in Opcode::all() {
975 assert_eq!(Opcode::from_name(op.name()), Some(op));
976 }
977 assert_eq!(Opcode::from_name("phi"), None);
978 assert_eq!(Opcode::from_name("getelementptr"), None);
979 assert_eq!(Opcode::from_name(""), None);
980 }
981
982 #[test]
983 fn the_terminators_are_the_ones_control_leaves_by() {
984 let terminators: Vec<&str> =
985 Opcode::all().filter(|op| op.is_terminator()).map(Opcode::name).collect();
986 assert_eq!(
987 terminators,
988 ["jump", "br_if", "switch", "indirect_br", "return", "unreachable", "tail_call"]
989 );
990 }
991
992 #[test]
993 fn a_terminator_produces_nothing() {
994 for op in Opcode::all().filter(|op| op.is_terminator()) {
995 assert_eq!(op.results(), Some(0), "{op}");
996 }
997 }
998
999 #[test]
1000 fn the_pair_producing_opcodes_are_the_ones_with_a_flag_beside_the_value() {
1001 let pairs: Vec<&str> =
1002 Opcode::all().filter(|op| op.results() == Some(2)).map(Opcode::name).collect();
1003 assert_eq!(
1004 pairs,
1005 [
1006 "cmpxchg",
1007 "sadd_overflow",
1008 "uadd_overflow",
1009 "ssub_overflow",
1010 "usub_overflow",
1011 "smul_overflow",
1012 "umul_overflow"
1013 ]
1014 );
1015 }
1016
1017 #[test]
1018 fn memory_has_effects_and_arithmetic_does_not() {
1019 for op in [Opcode::Load, Opcode::Store, Opcode::Call, Opcode::Alloca, Opcode::Fence] {
1020 assert!(op.has_effects(), "{op}");
1021 }
1022 for op in [Opcode::Add, Opcode::FDiv, Opcode::ICmp, Opcode::PtrAdd, Opcode::IConst] {
1023 assert!(!op.has_effects(), "{op}");
1024 }
1025 }
1026
1027 #[test]
1028 fn commuting_is_only_claimed_where_it_holds() {
1029 assert!(Opcode::Add.is_commutative());
1030 assert!(Opcode::FAdd.is_commutative());
1031 assert!(!Opcode::Sub.is_commutative());
1032 assert!(!Opcode::FDiv.is_commutative());
1033 assert!(!Opcode::Shl.is_commutative());
1034 }
1035
1036 #[test]
1037 fn an_integer_predicate_inverts_and_swaps_back_to_itself() {
1038 for pred in IntPred::all() {
1039 assert_eq!(pred.inverse().inverse(), pred);
1040 assert_eq!(pred.swapped().swapped(), pred);
1041 assert_eq!(IntPred::from_name(pred.name()), Some(pred));
1042 }
1043 assert_eq!(IntPred::Slt.inverse(), IntPred::Sge);
1044 assert_eq!(IntPred::Slt.swapped(), IntPred::Sgt);
1045 assert_eq!(IntPred::from_name("lt"), None);
1046 }
1047
1048 #[test]
1049 fn a_floating_predicate_inverts_across_the_ordered_line() {
1050 for pred in FloatPred::all() {
1051 assert_eq!(pred.inverse().inverse(), pred);
1052 assert_eq!(pred.swapped().swapped(), pred);
1053 assert_eq!(FloatPred::from_name(pred.name()), Some(pred));
1054 }
1055 // Inverting has to cross the line, because the negation of an ordered comparison is
1056 // true when an operand is a NaN. This is where `!(a < b)` stops being `a >= b`. The
1057 // two constants are outside it: neither of them looks at its operands.
1058 for pred in FloatPred::all().filter(|p| !matches!(p, FloatPred::False | FloatPred::True)) {
1059 assert_ne!(pred.is_ordered(), pred.inverse().is_ordered(), "{pred}");
1060 }
1061 assert_eq!(FloatPred::Olt.inverse(), FloatPred::Uge);
1062 assert_eq!(FloatPred::Olt.swapped(), FloatPred::Ogt);
1063 }
1064
1065 #[test]
1066 fn swapping_a_predicate_keeps_it_ordered_or_unordered() {
1067 for pred in FloatPred::all() {
1068 assert_eq!(pred.is_ordered(), pred.swapped().is_ordered(), "{pred}");
1069 }
1070 for pred in IntPred::all() {
1071 assert_eq!(pred.is_signed(), pred.swapped().is_signed(), "{pred}");
1072 }
1073 }
1074
1075 #[test]
1076 fn no_two_predicates_share_a_name_within_their_family() {
1077 for names in [
1078 IntPred::all().map(IntPred::name).collect::<Vec<_>>(),
1079 FloatPred::all().map(FloatPred::name).collect::<Vec<_>>(),
1080 ] {
1081 let total = names.len();
1082 let mut names = names;
1083 names.sort_unstable();
1084 names.dedup();
1085 assert_eq!(names.len(), total);
1086 }
1087 }
1088}