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