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