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