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