cove_ir/inst.rs
1//! The instructions.
2//!
3//! Every instruction names its operands and its destination by **slot
4//! number**. There is no operand stack: no push, no pop, no stack-effect
5//! table, no discipline to get wrong.
6//!
7//! That is [ADR 0034](../../../docs/adr/0034-one-physical-word-stack.md)'s
8//! *"parameters, locals, temporaries and captures share the one slot
9//! numbering"* taken literally. If a temporary is a slot, then an
10//! instruction that consumes a temporary names a slot, and the thing an
11//! operand stack exists to provide is already there.
12//!
13//! Two things fall out of that, and they are why it is worth choosing:
14//!
15//! - **A frame's roots are a static fact.** A stack machine's set of live
16//! references changes as operands are pushed and popped, so its reference
17//! map has to be indexed by program counter. Here the map does not change
18//! between a function's first instruction and its last, and
19//! [`crate::RefMap`] is one bit per slot.
20//! - **A call needs no argument buffer.** The callee's frame begins where
21//! the caller's ends, so [`Inst::Call`] copies the words of argument *i*
22//! into the run parameter *i* occupies and transfers control. Nothing is
23//! pushed, permuted, or copied back.
24//!
25//! # The instruction set describes families, not cases
26//!
27//! There is one `LoadField`, not one per value kind that has fields; one
28//! `Arith`, not one per numeric type; one `Alloc`, not one per collection.
29//! A field of an *inline* value needs no instruction at all — it is a slot
30//! offset the lowering computes.
31//! What an object *is* is a question the object answers at run time, from
32//! its own header. Nothing here grows a case because a corpus program was
33//! refused, because nothing here refuses anything.
34//!
35//! The two instructions that carry an immediate — [`Inst::ArithImm`] and
36//! [`Inst::CmpImm`] — are the same rule applied to an operand rather than to
37//! a type. They are not `add.int.imm`, `sub.int.imm`, `lt.int.imm` and eight
38//! more: the operator is a field, as it already is on [`Inst::Arith`] and
39//! [`Inst::Cmp`], so the family stays two however many operators the language
40//! grows. What they say that no other instruction can is that an operand is a
41//! constant, which is a fact the source stated and every other representation
42//! of it throws away.
43
44use crate::layout::LayoutId;
45use crate::{ArgsId, BuiltinId, CaseId, FunctionId, HostOpId, StrId, TableId};
46
47/// A slot in the current frame: `memory[frame_base + slot]`.
48pub type Slot = u32;
49
50/// An index into a function's instructions.
51pub type Pc = u32;
52
53/// Which numeric interpretation an arithmetic or comparison instruction
54/// gives its operand words.
55#[derive(Clone, Copy, Debug, PartialEq, Eq)]
56pub enum Num {
57 /// Two's-complement `i64`. Also what a `Duration` is arithmetic on:
58 /// nanoseconds add like integers, and only the boundary cares that the
59 /// answer is called a `Duration`.
60 Int,
61 /// An IEEE-754 double, bit-cast out of the word.
62 Float,
63}
64
65/// What a comparison compares.
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub enum Compare {
68 Int,
69 Float,
70 Bool,
71 /// The bytes of two [`crate::Shape::Str`] objects.
72 Str,
73 /// Two words, as words.
74 ///
75 /// This is `is`: the identity comparison the language reserves for
76 /// shared storage, and it is the one comparison that is allowed to look
77 /// at a reference as bits, because that is what it is asking about.
78 Identity,
79 /// Two case indices, as the integers they are.
80 ///
81 /// [`Inst::Tag`] says a tag is refused by arithmetic, ordering and
82 /// integer comparison, because none of those accepts that `Repr` — and
83 /// that refusal is what keeps a case index from being confused with a
84 /// number. This does not weaken it: it accepts a `Tag` and nothing else,
85 /// so the pairing a tag can take part in is still only with another tag.
86 ///
87 /// What it is for is `Kind.Space == Kind.Word`, which is `1 == 2`. An
88 /// enum with no payload is one word wide and that word is the
89 /// discriminant, so two of them are equal exactly when the two words are.
90 /// Walked instead by `Any.equals`, the same question measured 342 ms
91 /// against 210 for 2,000,000 comparisons — 66 ns of builtin call apiece,
92 /// and 8% of a native profile of a formatter that reads a token's kind in
93 /// every loop it has.
94 Tag,
95}
96
97#[derive(Clone, Copy, Debug, PartialEq, Eq)]
98pub enum ArithOp {
99 Add,
100 Sub,
101 Mul,
102 Div,
103 Rem,
104}
105
106#[derive(Clone, Copy, Debug, PartialEq, Eq)]
107pub enum CmpOp {
108 Eq,
109 Ne,
110 Lt,
111 Le,
112 Gt,
113 Ge,
114}
115
116/// A conversion between two scalar representations.
117#[derive(Clone, Copy, Debug, PartialEq, Eq)]
118pub enum Convert {
119 /// `Int` to `Float`, as `as`-style widening.
120 IntToFloat,
121 /// `Float` to `Int`, truncating toward zero.
122 FloatToInt,
123}
124
125/// How many elements an [`Inst::Alloc`] asks for.
126#[derive(Clone, Copy, Debug, PartialEq, Eq)]
127pub enum Len {
128 /// A shape whose size the layout already fixes: a struct, an enum, a
129 /// closure, a box.
130 Fixed,
131 /// A count the lowering knew: a literal array's element count, a string
132 /// literal's byte count.
133 Count(u32),
134 /// A count in a slot, as an `Int`.
135 Slot(Slot),
136}
137
138/// One instruction.
139#[derive(Clone, Debug, PartialEq)]
140pub enum Inst {
141 // ---- constants and moves ------------------------------------------
142 /// `dst = ()`
143 Unit { dst: Slot },
144 /// `dst = value`
145 Bool { dst: Slot, value: bool },
146 /// `dst = value`, also how a `Duration` literal reaches a slot.
147 Int { dst: Slot, value: i64 },
148 /// `dst = <callee's dense id>`, as a word.
149 ///
150 /// What a lowered closure's environment is given for its callee field,
151 /// and the only place that value is produced. It is not [`Inst::Int`],
152 /// though the word it writes is the same [`FunctionId`] an `Int` of that
153 /// value would be: a closure's [`crate::layout::Shape::Closure`] already
154 /// carries `function: FunctionId` as a typed fact, and writing the same
155 /// id again through the untyped integer path made a second,
156 /// uninspectable copy of it — one no verifier could tell from an
157 /// ordinary integer, and one that renumbered every golden lowering with
158 /// a closure in it whenever an unrelated function was added or moved,
159 /// because the two facts were spelled a plain number in the listing
160 /// rather than the name that number happened to hold that day.
161 ///
162 /// [`mod@crate::verify`] bounds `callee` against
163 /// [`crate::program::Program::functions`] the way [`Inst::Call`]'s is
164 /// bounded, and, where the destination is then stored into a
165 /// statically-known closure object, checks that `callee` agrees with
166 /// what the object's own layout says — the comparison the two copies
167 /// never had. [`crate::print`] renders it symbolically, by the callee's
168 /// name and not its number, which is what stops the churn: an unrelated
169 /// declaration changing `callee`'s numeric value no longer changes a
170 /// single character of the listing.
171 FuncRef { dst: Slot, callee: FunctionId },
172 /// `dst = <the index of `case` in `layout`>`, as an enum's discriminant.
173 ///
174 /// The one way a discriminant is written. It is not [`Inst::Int`], though
175 /// the word it writes is the number an `Int` of that value would write,
176 /// and the reason is [`Inst::FuncRef`]'s: a case index reached its slot
177 /// through the untyped integer path, where no verifier could tell it from
178 /// an ordinary number and nothing bounded it against the enum it was
179 /// supposed to name.
180 ///
181 /// Its destination is a [`Repr::Tag`](crate::Repr::Tag) word, which is
182 /// what makes the two facts separable at all: a tag is one non-reference
183 /// word, physically an integer, and is refused by arithmetic, ordering
184 /// and integer comparison because none of those accepts that `Repr`.
185 /// [`Inst::Switch`] accepts it, and so do copying and clearing, which
186 /// read a layout rather than a `Repr`.
187 ///
188 /// [`mod@crate::verify`] bounds `case` against `layout`'s own case list
189 /// and refuses a `layout` that is not an enum. [`crate::print`] renders
190 /// it by the case's name, so an unrelated case added before it changes no
191 /// character of a listing that does not mention it.
192 Tag {
193 dst: Slot,
194 layout: LayoutId,
195 case: CaseId,
196 },
197 /// `dst = f64::from_bits(bits)`
198 ///
199 /// The bits rather than the `f64` so that [`Inst`] can be `Eq` and
200 /// `Hash`ed, and so that a NaN in the source survives the IR unchanged.
201 Float { dst: Slot, bits: u64 },
202 /// `dst = <the address of the string object for `text`>`
203 ///
204 /// The object already exists.
205 /// [ADR 0045](../../../docs/adr/0045-a-literal-is-there-before-the-program-runs.md)
206 /// places every program literal in the heap before the run's first
207 /// instruction executes, so this is a load of a precomputed address —
208 /// no branch, no allocation, no copy, whether this is the first turn of
209 /// a loop or the millionth.
210 Str { dst: Slot, text: StrId },
211 /// `dst = src`, for the words `layout` describes.
212 ///
213 /// This is ADR 0001's field-wise shallow copy, and it is one operation
214 /// because a value's words are where the value is. Copying a
215 /// `Wrapper { p: Point, v: Vector }` copies three words: the `Point`
216 /// becomes independent because its words were copied, and the `Vector`
217 /// stays shared because what was copied is its address. Both answers
218 /// fall out of the same copy and neither needs a policy.
219 ///
220 /// There is no sharing bit, no copy-on-write and no unsharing of a write
221 /// path. Those were needed only while every struct was one address, and
222 /// they existed to conceal an alias the representation had created.
223 ///
224 /// `let` and `var` lower to the same thing: ADR 0001 says they do not
225 /// change expression semantics, and Cove has no move semantics. A
226 /// lowering may elide a copy whose source is a fresh temporary, but that
227 /// is an optimisation — correctness never depends on proving uniqueness,
228 /// and a lowering that cannot tell emits the copy.
229 Copy {
230 dst: Slot,
231 src: Slot,
232 layout: LayoutId,
233 },
234 /// Zeroes the words `layout` describes at `slot`.
235 ///
236 /// A slot whose value is dead. The lowering emits one at the end of the
237 /// scope a binding belonged to, and at a temporary's last use, for every
238 /// slot whose [`Repr`](crate::Repr) is [`Ref`](crate::Repr::Ref) or
239 /// [`Addr`](crate::Repr::Addr).
240 ///
241 /// This is what keeps a static reference map from turning into a leak.
242 /// The map says which slots the collector *reads*; it cannot say when
243 /// the value in one stopped being needed, because that is a fact about a
244 /// program point and the map is a fact about a function. Clearing the
245 /// slot moves the answer into the data: a dead reference slot holds
246 /// null, the collector reads null, and the object is unreachable at the
247 /// next collection rather than at the next return.
248 ///
249 /// It costs one store on a path that was going to leave the value behind
250 /// anyway, and it is emitted only where the slot would otherwise retain
251 /// something — never for a scalar, and never where the slot is about to
252 /// be overwritten.
253 Clear { slot: Slot, layout: LayoutId },
254
255 // ---- scalar operations --------------------------------------------
256 /// `dst = -a`
257 Neg { num: Num, dst: Slot, a: Slot },
258 /// `dst = a op b`
259 Arith {
260 num: Num,
261 op: ArithOp,
262 dst: Slot,
263 a: Slot,
264 b: Slot,
265 },
266 /// `dst = a op b`, answering a `Bool`.
267 Cmp {
268 on: Compare,
269 op: CmpOp,
270 dst: Slot,
271 a: Slot,
272 b: Slot,
273 },
274 /// `dst = a op value`, on `Int` words, where `value` was written in the
275 /// source.
276 ///
277 /// The same arithmetic [`Inst::Arith`] does — the same overflow, the same
278 /// division and remainder by zero, the same `Duration` naming — with the
279 /// right operand in the instruction instead of in a slot. It exists
280 /// because the alternative is worse than a wasted word: the literals of a
281 /// loop condition are materialised by instructions the back edge jumps
282 /// over, so `while i < 2000000` executed an [`Inst::Int`] two million
283 /// times to write a constant into a temporary that nothing else ever
284 /// read.
285 ///
286 /// **Two variants and not sixteen.** `op` is a field here exactly as it
287 /// is on [`Inst::Arith`], so an operator added to the language costs no
288 /// instruction, and this pair covers the eleven that exist.
289 ///
290 /// **The immediate is on the right, and only on the right.** `a - 1` and
291 /// `1 - a` are different questions and `a % 7` and `7 % a` more so, so a
292 /// left-hand immediate would be a second family rather than a mirror of
293 /// this one; a commutative operator's lowering puts the literal on the
294 /// right instead. There is no float immediate for the same reason there
295 /// is no left one — a second family, for a form no benchmark asked for.
296 ///
297 /// `Num` is absent because there is only [`Num::Int`] to name: `value` is
298 /// an `i64`, and a `Duration`'s word is nanoseconds, which is an `i64`.
299 ArithImm {
300 op: ArithOp,
301 dst: Slot,
302 a: Slot,
303 value: i64,
304 },
305 /// `dst = a op value`, comparing `Int` words, answering a `Bool`.
306 ///
307 /// [`Inst::ArithImm`]'s other half, and `Compare` is absent for the
308 /// reason `Num` is absent there: the operand is an `Int` word, so the
309 /// comparison is [`Compare::Int`].
310 CmpImm {
311 op: CmpOp,
312 dst: Slot,
313 a: Slot,
314 value: i64,
315 },
316 /// `dst = !a`
317 Not { dst: Slot, a: Slot },
318 /// `dst = <a, converted>`
319 Convert { to: Convert, dst: Slot, a: Slot },
320
321 // ---- control flow --------------------------------------------------
322 /// Continue at `to`.
323 Jump { to: Pc },
324 /// Continue at `to` when `cond` is false; otherwise fall through.
325 ///
326 /// One conditional branch rather than two: `&&`, `||`, `if` and `while`
327 /// all lower through it, and the lowering inverts the condition rather
328 /// than the instruction set carrying both polarities.
329 BranchFalse { cond: Slot, to: Pc },
330 /// Continue at the entry of `table` selected by the `Int` in `on`.
331 ///
332 /// This is how a `match` over an enum's cases dispatches: `on` is the
333 /// case index read out of the object, and the table has one target per
334 /// case plus a default.
335 Switch { on: Slot, table: TableId },
336 /// Leave the function, answering the value at `src`.
337 ///
338 /// `src` is the *first* slot of that value location, and how many words
339 /// follow it is [`crate::Function::returns`] — which is why a listing
340 /// writes that layout *on* the slot, and the whole run with it:
341 /// `return s0..s2:Result`.
342 Return { src: Slot },
343
344 // ---- calls ----------------------------------------------------------
345 /// `dst = callee(args...)`
346 ///
347 /// The machine writes `args[i]` into the callee's slot `i` and gives it
348 /// a frame beginning at the end of this one. Nothing else happens: the
349 /// argument list is static, the destination is declared, and there is no
350 /// buffer between the two frames.
351 Call {
352 dst: Slot,
353 callee: FunctionId,
354 args: ArgsId,
355 },
356 /// `dst = closure(args...)`, where `closure` holds a reference to a
357 /// [`crate::Shape::Closure`] object.
358 ///
359 /// The callee is the function id in the object's first payload word, and
360 /// its captures are copied into the slots after the parameters.
361 ///
362 /// `result` is the layout of what the call answers, and it is the one
363 /// operand here that is not read off the object at run time. Which body
364 /// this enters is a run-time fact; how wide its answer is, is not. The
365 /// checker settles a call through a value against the callee's *function
366 /// type*, so the answer's type — and with it the run of words the
367 /// destination has to be — is as static as any other call's.
368 ///
369 /// It is carried rather than looked up because there is nowhere to look:
370 /// every other call names a callee the program declares — a
371 /// [`FunctionId`], a [`crate::HostOpId`], a [`crate::BuiltinId`] — and
372 /// the answer's layout is read from that declaration. A closure call
373 /// names a word in a slot. Without this field the destination's width
374 /// was known to the checker, thrown away by the lowering, and then
375 /// unavailable to everything downstream: `crate::verify` could ask only
376 /// that `dst` was a slot at all, the encoded verifier the same, and a
377 /// listing had to print the head word's `Repr` where every other call
378 /// prints the run. A two-word answer written into the last slot of a
379 /// frame was checked by nothing.
380 CallClosure {
381 dst: Slot,
382 closure: Slot,
383 args: ArgsId,
384 result: LayoutId,
385 },
386 /// `dst = <host op>(args...)`
387 ///
388 /// This is a boundary: the arguments are materialised into public
389 /// public `Value`s, the host answers one, and the answer
390 /// is written back into a word. It is the only place in ordinary
391 /// execution where a `Value` exists.
392 CallHost {
393 dst: Slot,
394 op: HostOpId,
395 args: ArgsId,
396 },
397 /// `dst = <host op>(*receiver, args...)`, addressed to the resource
398 /// the [`Repr::Host`](crate::Repr::Host) word in `receiver` names.
399 ///
400 /// The same boundary [`Inst::CallHost`] is, reached the other way a
401 /// callee can be found. `Call` and `CallClosure` are already that pair
402 /// on this side of the boundary — a callee named statically, and a
403 /// callee in a slot — and a host resource's operations are the same
404 /// distinction one boundary further out: ADR 0013 gives the *host* the
405 /// table of what is open, so `files.Writer.writeLine` is dispatched on
406 /// the handle and not on the module the source wrote in front of it.
407 ///
408 /// The receiver is an operand of its own rather than `args[0]`, and that
409 /// is the difference that decides there are two instructions here rather
410 /// than a flag on one. An [`crate::Arg`] is a value location the
411 /// boundary *materialises*, and the registry does not take the handle as
412 /// an argument — `HostRegistry::call_resource` takes it as the thing
413 /// being addressed and hands the host only what follows. So putting it
414 /// in the list would mean materialising a name into a `Value` in order
415 /// to take it apart again, and the argument list would no longer be the
416 /// arguments.
417 CallResource {
418 dst: Slot,
419 receiver: Slot,
420 op: HostOpId,
421 args: ArgsId,
422 },
423 /// `dst = <builtin>(args...)`
424 ///
425 /// A builtin operates on words and heap objects directly. It is not a
426 /// boundary and it does not materialise anything.
427 CallBuiltin {
428 dst: Slot,
429 builtin: BuiltinId,
430 args: ArgsId,
431 },
432
433 // ---- the heap --------------------------------------------------------
434 /// `dst = <a new object of `layout`>`
435 ///
436 /// The payload is zeroed, so a reference field of a half-built object is
437 /// null rather than garbage if a collection happens before it is
438 /// filled in.
439 Alloc {
440 dst: Slot,
441 layout: LayoutId,
442 len: Len,
443 },
444 /// `dst = <the value at payload word `at` of `obj`>`
445 ///
446 /// One instruction for every fixed-position read there is: a struct
447 /// field, an enum's case index (`at == 0`) or payload word, a closure's
448 /// capture. The lowering computes `at` from the layout it knows
449 /// statically; the machine bounds-checks it against the layout the
450 /// object names, because a reference slot carries no layout of its own.
451 LoadField {
452 dst: Slot,
453 obj: Slot,
454 at: u32,
455 layout: LayoutId,
456 },
457 /// `<payload word `at` of `obj`> = src`
458 StoreField {
459 obj: Slot,
460 at: u32,
461 src: Slot,
462 layout: LayoutId,
463 },
464 /// `dst = obj[index]`, for an object whose elements are `layout` wide.
465 ///
466 /// The stride is the element layout's width, so an `Array<Point>` is a
467 /// run of two-word elements rather than a run of addresses.
468 LoadElem {
469 dst: Slot,
470 obj: Slot,
471 index: Slot,
472 layout: LayoutId,
473 },
474 /// `obj[index] = src`
475 StoreElem {
476 obj: Slot,
477 index: Slot,
478 src: Slot,
479 layout: LayoutId,
480 },
481 /// `dst = <byte `at` of the string `obj`>`, as an `Int` in `0..=255`.
482 ///
483 /// The one instruction that reaches *inside* a word. Everything else here
484 /// addresses a value location or a payload word, because a word is what a
485 /// frame and a heap object are made of — but a `String`'s payload is
486 /// bytes, eight to a word, and the only shape that reads one is this.
487 ///
488 /// It is an instruction and not a builtin, and the difference is the
489 /// whole reason it exists. `String.byteAt` as a `call-builtin` measured
490 /// 58 ns of which 48 ns was *being a builtin call* — the operands copied
491 /// into a buffer, the operand array built, the dispatch by two strings,
492 /// the answer written back — for work that is one payload word, a shift
493 /// and a mask. `benches/builtincall` is where those two numbers are.
494 ///
495 /// `at` is bounds-checked against the receiver's byte length, and an
496 /// offset outside it stops the run. That is `String.sliceBytes`'s rule
497 /// and not `Array.get`'s: a byte offset out of range is one this type
498 /// never handed out, where an index out of range is arithmetic a caller
499 /// did about a sequence it can count. Answering an `Option` here would
500 /// also be answering it eight times per word of a lexer's inner loop,
501 /// and the wrapper was measured at more than the read.
502 ByteAt { dst: Slot, obj: Slot, at: Slot },
503 /// `dst = <a new, zeroed byte run of `len` bytes>`.
504 ///
505 /// [ADR 0051](../../../docs/adr/0051-a-string-is-built-as-a-byte-run.md)'s
506 /// allocation. It always allocates [`crate::Program::bytes_layout`] —
507 /// the one shape every run under construction shares — so unlike
508 /// [`Inst::Alloc`] it carries no [`LayoutId`] of its own, for
509 /// [`Inst::Str`]'s reason: a program-wide constant should not have to be
510 /// named at every call site that always means the same one.
511 ///
512 /// The payload is zeroed exactly as [`Inst::Alloc`]'s is, so a run that
513 /// is collected before it is filled walks safely — not because a
514 /// half-written byte is meaningful, but because [`crate::Shape::Bytes`] holds no
515 /// references for the collector to chase either way.
516 ///
517 /// `len` is a byte count and a run-time value, because the whole point
518 /// of ADR 0051's construction is a length computed by summing the pieces
519 /// a `join` was given — a fixed length would have made this
520 /// [`Inst::Alloc`] with a [`Len::Count`] instead. A negative or oversized
521 /// `len` fails through the same "this run has no memory left" refusal
522 /// every other allocation does.
523 AllocBytes { dst: Slot, len: Slot },
524 /// `bytes[at] = value`, one checked byte of a run under construction.
525 ///
526 /// The scalar half of [ADR 0051](../../../docs/adr/0051-a-string-is-built-as-a-byte-run.md)'s
527 /// two write primitives, and deliberately the smaller one: it exists for
528 /// a delimiter or an encoded scalar a lowering writes one at a time, not
529 /// as how a `join` is expected to move text. Copying more than a
530 /// handful of bytes through this would replace one native copy with as
531 /// many dispatches as there are bytes, which is exactly the shape
532 /// [`Inst::CopyBytes`] exists to avoid.
533 ///
534 /// `bytes` must name a live [`crate::Shape::Bytes`] object — writing into a
535 /// `String` is refused, because a `String`'s bytes are the invariant
536 /// [`Inst::FinishString`] exists to establish and never to reopen.
537 /// `at` is bounds-checked against the run's declared length the same way
538 /// [`Inst::ByteAt`]'s is, and `value` must be a byte, `0..=255`: neither
539 /// bound is optional here the way it would be reading back a value this
540 /// run already produced, because this is the instruction that puts an
541 /// arbitrary integer into memory another instruction will one day read
542 /// back and trust.
543 WriteByte { bytes: Slot, at: Slot, value: Slot },
544 /// A bulk range copy into a run under construction: `dst[dst_at
545 /// .. dst_at+len] = src[src_at .. src_at+len]`.
546 ///
547 /// This is [ADR 0051](../../../docs/adr/0051-a-string-is-built-as-a-byte-run.md)'s
548 /// principal instruction — the one a `join` or a fused `sliceBytes`
549 /// lowers to instead of `sliceBytes -> Vector.push -> join`'s hidden
550 /// allocations — and the reason it exists at all is that a byte loop
551 /// over [`Inst::WriteByte`] would multiply dispatch by the number of
552 /// bytes moved, which ADR 0051's "why a byte loop in IR is not enough"
553 /// rejects. One instruction, one native run copy.
554 ///
555 /// # What it costs, and what it is charged
556 ///
557 /// One dispatch and one unit of work per payload word moved, which is
558 /// [ADR 0052](../../../docs/adr/0052-a-growable-value-is-a-stable-owner-over-a-replaceable-run.md)'s
559 /// "charged proportionally to the bytes or words examined". A word is the
560 /// unit because a word is what the memory moves.
561 ///
562 /// The charge is not folded into the count of instructions dispatched.
563 /// That number is a public observable — the debugger, the trace, the
564 /// profile and `cove-bench` all report it, and `crate::vm::profile`'s own
565 /// test asserts its per-opcode totals sum to it — so weighted work has a
566 /// coordinate of its own.
567 ///
568 /// The copy is made in bounded chunks with a safepoint between them, and
569 /// that is not a refinement of the charge but the thing that makes it
570 /// sound. A charge taken only *after* an arbitrarily large copy would let
571 /// one instruction run arbitrarily far past a fuel or cancellation bound
572 /// before anything looked, which
573 /// [ADR 0040](../../../docs/adr/0040-a-bound-outlives-its-backend.md)'s
574 /// `S + T` forbids. One chunk is one stride of work, so a stopped run gets
575 /// no further than a stride past the bound whatever length it was given.
576 ///
577 /// A collection may therefore happen with the destination half written.
578 /// That is safe for the reason ADR 0051 gave for the run's payload holding
579 /// no references, and rooted for a second one: the caller has already
580 /// `sync`ed, and both objects are named by frame slots this instruction
581 /// read them out of, so the walk finds them where it finds every other
582 /// live reference.
583 ///
584 /// [`Inst::AllocBytes`] and [`Inst::FinishString`] are **not** charged
585 /// this way and not chunked. Their bulk work is inside the allocator's
586 /// zeroing and inside one `from_utf8` over a copy of the run, neither of
587 /// which this could interrupt, and charging an operation that cannot be
588 /// interrupted only makes its overshoot visible rather than bounded. They
589 /// remain one unit each, which is what an ordinary [`Inst::Alloc`] of a
590 /// large `Array` has always been.
591 ///
592 /// `src` may be a `String` **or** another [`crate::Shape::Bytes`] run — a fused
593 /// slice copies straight out of the run that produced it, without
594 /// finishing it as a `String` first — but `dst` must always be a
595 /// [`crate::Shape::Bytes`] run under construction: writing into a `String` is
596 /// refused for [`Inst::WriteByte`]'s reason. Bounds are checked against
597 /// both objects' declared lengths rather than left to whatever the
598 /// native copy routine happens to do with an out-of-range range.
599 ///
600 /// # Why five operands live behind an [`ArgsId`]
601 ///
602 /// An encoded instruction has room for three slot-sized operands and a
603 /// payload, and this needs five: `dst`, `dst_at`, `src`, `src_at` and
604 /// `len`. Rather than spend a fifth [`Inst`] variant or a second
605 /// instruction pair to carry the overflow, this reuses the machinery a
606 /// call's argument list already is — [`ArgsId`] names a row of
607 /// [`crate::Program::args`], and a call already demonstrates that an
608 /// arity larger than three operands is a solved problem in this format.
609 /// The row holds exactly five [`crate::Arg`]s, in the order `dst`,
610 /// `dst_at`, `src`, `src_at`, `len`, and carries each one's layout the
611 /// same way a call's arguments do, so the verifier checks them by the
612 /// same rule rather than by a new one.
613 CopyBytes { args: ArgsId },
614 /// `dst = <the run at `bytes`, validated and turned into an immutable
615 /// String, in place>`.
616 ///
617 /// The instruction [ADR 0051](../../../docs/adr/0051-a-string-is-built-as-a-byte-run.md)
618 /// closes construction with. `bytes` must name a live [`crate::Shape::Bytes`]
619 /// run; its packed payload is read and checked as UTF-8 exactly once,
620 /// because a run assembled from [`Inst::WriteByte`] and [`Inst::CopyBytes`]
621 /// may hold anything a byte can hold, and ADR 0051 refuses to skip that
622 /// check for an arbitrary run. Invalid UTF-8 fails with the same error a
623 /// source-level string operation already raises for it.
624 ///
625 /// On success the run becomes the answer **without copying its
626 /// payload**: a [`crate::Shape::Bytes`] object and a [`crate::Shape::Str`] object of
627 /// the same byte length occupy the same number of words, so finishing is
628 /// a re-label of the object's header — its layout changes from
629 /// [`crate::Program::bytes_layout`] to [`crate::Program::str_layout`] and
630 /// its `len` does not change at all — rather than an allocation and a
631 /// copy. Not copying the payload is the whole performance argument this
632 /// ADR makes: every byte a `join` moves is moved once, by
633 /// [`Inst::CopyBytes`], and finishing moves none of them again.
634 FinishString { dst: Slot, bytes: Slot },
635 /// `dst = <a new, empty byte buffer whose store has room for `capacity`
636 /// bytes>`.
637 ///
638 /// [ADR 0052](../../../docs/adr/0052-a-growable-value-is-a-stable-owner-over-a-replaceable-run.md)'s
639 /// allocation, and the first of the four instructions that replace
640 /// [`Inst::AllocBytes`] wherever the final length is not known before the
641 /// writes. ADR 0051's fixed run is enough when it *is* known; it is not
642 /// enough for `examples/covefmt`, whose three hot joins are filled by
643 /// data-dependent loops and whose largest is a `var out` parameter passed
644 /// through recursive calls.
645 ///
646 /// Two objects are allocated, because that is what a stable owner is: the
647 /// owner is [`crate::Program::buffer_layout`], two payload words holding a
648 /// logical length and a reference; the store is
649 /// [`crate::Program::bytes_layout`], the same packed run ADR 0051 already
650 /// has, whose *header* length is the capacity. Neither layout is named
651 /// here, for [`Inst::AllocBytes`]'s reason: both are program-wide
652 /// constants, and a call site that always means the same one should not
653 /// have to say so.
654 ///
655 /// `capacity` is a **hint** and not a bound. Exceeding it grows the store
656 /// rather than failing, so a tuning estimate cannot change what a program
657 /// answers — which is the whole of ADR 0052's "capacity is not an Array
658 /// length". A capacity below the runtime's own floor is raised to it, and a
659 /// negative or oversized one fails through the same "this run has no memory
660 /// left" refusal every other allocation does.
661 AllocBuffer { dst: Slot, capacity: Slot },
662 /// `buffer.append(value)`, one checked byte onto the end of a buffer.
663 ///
664 /// The scalar half of ADR 0052's append pair, and [`Inst::WriteByte`]'s
665 /// counterpart for a growable run — with the one difference that makes a
666 /// buffer a buffer: there is no offset. A write goes at the logical length
667 /// and the logical length becomes one more, so a caller never names a
668 /// position and can never leave a hole below one.
669 ///
670 /// It exists for a delimiter or an encoded scalar a lowering emits one at a
671 /// time, not as how text is expected to move: copying a run of bytes
672 /// through this would be as many dispatches as there are bytes, which is
673 /// what [`Inst::AppendBytes`] is for.
674 ///
675 /// `buffer` must name a live owner and `value` must be a byte, `0..=255`,
676 /// for [`Inst::WriteByte`]'s reason — this is an instruction that puts an
677 /// arbitrary integer into memory that [`Inst::FinishBuffer`] will later
678 /// read back and validate. Nothing is bounds-checked against the capacity,
679 /// because there is no bound to check: a full store grows.
680 AppendByte { buffer: Slot, value: Slot },
681 /// A bulk range append: `buffer.append(src[from .. to])`.
682 ///
683 /// ADR 0052's principal instruction, and [`Inst::CopyBytes`]'s growable
684 /// counterpart. One dispatch moves the whole range, for the reason ADR
685 /// 0051 gave when it refused a byte loop in IR: a loop of
686 /// [`Inst::AppendByte`] would multiply dispatch by the number of bytes.
687 ///
688 /// `src` may be a `String` **or** a [`crate::Shape::Bytes`] run, which is
689 /// what lets a fused slice copy straight out of the run or the string that
690 /// produced it. The ADR's own example is the optimisation this enables:
691 /// `sliceBytes(source, from, to) -> append` becomes one checked append
692 /// from that source range and never materialises the slice.
693 ///
694 /// Where `src` is a `String`, `from` and `to` are checked to be character
695 /// boundaries and not merely in range — the same check, in the same words,
696 /// that `String.sliceBytes` makes. ADR 0052 requires it: "`appendSlice`
697 /// checks the same bounds and UTF-8 boundaries as `String.sliceBytes`".
698 /// Without it a program could assemble a run of valid pieces that is not
699 /// valid UTF-8, and discover it only at [`Inst::FinishBuffer`], where the
700 /// offset that did it is long gone. A [`crate::Shape::Bytes`] source is
701 /// held to no such rule, because a run under construction is not claiming
702 /// to be text.
703 ///
704 /// # What it costs, and what it is charged
705 ///
706 /// [`Inst::CopyBytes`]'s answer, unchanged: one unit of work per payload
707 /// word moved, in bounded chunks with a safepoint between them, so a
708 /// stopped run gets no further than a stride past the bound whatever length
709 /// it was given. Growth is charged as the allocation it is.
710 ///
711 /// The store is grown **once, up front**, for the whole range rather than
712 /// per chunk. That is not only cheaper: a growth part way through would
713 /// have to copy a prefix that the chunks before it had already written, and
714 /// the one allocation before the first chunk is what keeps the copy a copy.
715 ///
716 /// # Why four operands live behind an [`ArgsId`]
717 ///
718 /// [`Inst::CopyBytes`]'s reason at one fewer operand: an encoded
719 /// instruction has room for three slot-sized operands and this needs four —
720 /// `buffer`, `src`, `from` and `to`. Rather than spend a second instruction
721 /// to carry the overflow, this reuses the machinery a call's argument list
722 /// already is. The row holds exactly four [`crate::Arg`]s in the order
723 /// `buffer`, `src`, `from`, `to`, and carries each one's layout the way a
724 /// call's arguments do, so the verifier checks them by the same rule.
725 AppendBytes { args: ArgsId },
726 /// `dst = <the buffer at `buffer`, consumed, its store validated and
727 /// relabelled into an immutable String>`.
728 ///
729 /// ADR 0052's finish, and [`Inst::FinishString`]'s counterpart for a
730 /// growable run. The bytes are read and checked as UTF-8 exactly once,
731 /// because a run assembled from [`Inst::AppendByte`] may hold anything a
732 /// byte can hold, and invalid UTF-8 fails with the same error a
733 /// source-level string operation already raises for it.
734 ///
735 /// What is validated and what is answered is the **live prefix**
736 /// `[0, length)`. A store is as long as the last growth made it, and ADR
737 /// 0052's "finishing reuses the store" is what happens to the rest: the
738 /// store is relabelled from [`crate::Program::bytes_layout`] to
739 /// [`crate::Program::str_layout`] with the *logical* length, and the words
740 /// between the two lengths become a free block the next sweep folds back
741 /// in. Nothing is copied, which is the same O(1) transition
742 /// `Vector.freeze()` already makes for elements.
743 ///
744 /// The owner is then emptied — length zero, store null — exactly as
745 /// `Vector.freeze()` empties a vector, because finishing *consumes*. That
746 /// the consumed buffer has no second live holder is
747 /// [`cove_sema`](../../../crates/cove-sema/src/unique.rs)'s conservative
748 /// local uniqueness proof and not something this machine can answer; what
749 /// the machine keeps is the liveness check, so a buffer used after a finish
750 /// is refused rather than read as an empty one.
751 ///
752 /// [`crate::Shape::Bytes`] cannot cross a Cove call and neither can the
753 /// owner cross the Host boundary, but the owner *can* cross a call, which
754 /// is the whole reason it is a value rather than a raw run: the formatter's
755 /// `fn emit(node: Tree, var out: StringBuilder)` needs to pass a partly
756 /// built string down a recursion.
757 FinishBuffer { dst: Slot, buffer: Slot },
758 /// `dst = <obj's header length>`: an element count, or a string's bytes.
759 Len { dst: Slot, obj: Slot },
760 /// `dst = <the [`LayoutId`] in obj's header>`, as an `Int`.
761 ///
762 /// The other half of the header word [`Inst::Len`] reads, and it is here
763 /// for the same reason: *what an object is* is a question the object
764 /// answers at run time, from its own header, and a `Ref` slot carries no
765 /// layout of its own.
766 ///
767 /// It exists because a dispatch has to ask it. A `dyn Trait` value's
768 /// implementation is decided by the type behind it, and nothing static
769 /// says which that is; the object's header does. Reading it into a slot
770 /// turns "which implementation" into an ordinary [`Inst::Switch`] over a
771 /// table the lowering builds from the trait's declared conformances,
772 /// which is why there is no dispatch instruction — one general question
773 /// about an object, answered with the control flow that is already here.
774 LayoutOf { dst: Slot, obj: Slot },
775
776 // ---- places ----------------------------------------------------------
777 /// `dst = &frame[slot]`
778 ///
779 /// A place is one word. There is no place object, no place stack and no
780 /// table of places; a `var` parameter is an ordinary slot whose
781 /// [`Repr`](crate::Repr) is [`Addr`](crate::Repr::Addr).
782 AddrOfSlot { dst: Slot, slot: Slot },
783 /// `dst = &<payload word `at` of `obj`>`
784 ///
785 /// The lowering keeps `obj` in a live reference slot for exactly the
786 /// address's live range, and clears that slot with [`Inst::Clear`] when
787 /// the address dies — not unconditionally for the rest of the frame,
788 /// which would retain the object across everything a long-running body
789 /// does afterwards. The collector therefore needs no interior-pointer
790 /// logic, and the heap does not move, so the address stays correct
791 /// across a collection for as long as it is live and no longer.
792 AddrOfField { dst: Slot, obj: Slot, at: u32 },
793 /// `dst = &obj[index]`, at a stride of `layout`'s width.
794 AddrOfElem {
795 dst: Slot,
796 obj: Slot,
797 index: Slot,
798 layout: LayoutId,
799 },
800 /// `dst = addr + at`, a static word offset into the value at `addr`.
801 ///
802 /// The one place instruction whose operand is itself a place, and what
803 /// makes a place composable. A place is the address of the *first* word
804 /// of a value location, so without this a `var` parameter could only name
805 /// the whole of what it was given: `p.y = 1` through a `var p: Point` had
806 /// to load both words, write one and store both back — observationally
807 /// the same on one thread, but not what the address was for — and
808 /// `f(var p.y)` could not be lowered at all, because there was no way to
809 /// form the address to pass.
810 ///
811 /// `at` is a word offset within the value the address names, computed by
812 /// the lowering from the layout the checker settled. It is the same
813 /// arithmetic a field of an inline struct is, done to an address instead
814 /// of to a slot number, and the answer is again the address of the first
815 /// word of a value location — so it goes back through [`Inst::Load`],
816 /// [`Inst::Store`] or another of these with no second rule about what an
817 /// address is.
818 ///
819 /// Nothing checks `at` against the value's extent, because a frame does
820 /// not record one: what an address names is a fact about the instruction
821 /// that formed it, and [`mod@crate::verify`] says the same of
822 /// [`Inst::Switch`]'s operand for the same reason.
823 AddrOfPart { dst: Slot, addr: Slot, at: u32 },
824 /// `dst = *addr`, for the words `layout` describes.
825 Load {
826 dst: Slot,
827 addr: Slot,
828 layout: LayoutId,
829 },
830 /// `*addr = src`, for the words `layout` describes.
831 ///
832 /// A nested write through a `var` parameter updates the destination words
833 /// in place. There is nothing between the address and the words, which is
834 /// what a place being an address of the *first word* of a value location
835 /// buys.
836 Store {
837 addr: Slot,
838 src: Slot,
839 layout: LayoutId,
840 },
841
842 // ---- erasure ----------------------------------------------------------
843 /// `dst = <a box holding the words of `src`, tagged `layout`>`
844 ///
845 /// What a value becomes when its static type is not known: `dyn Trait`,
846 /// a Host result a schema declared `Any`, an expression the checker
847 /// declined to type. One word in the slot either way.
848 Box {
849 dst: Slot,
850 src: Slot,
851 layout: LayoutId,
852 },
853 /// `dst = <the value inside the box in `src`>`, trapping if its tag is
854 /// not `layout`.
855 Unbox {
856 dst: Slot,
857 src: Slot,
858 layout: LayoutId,
859 },
860
861 // ---- tasks -------------------------------------------------------------
862 /// `dst = <a new task scope, open>`
863 ///
864 /// `scope name { ... }` binds one of these, and everything the Language
865 /// Card says about a scope is a fact about the two instructions that
866 /// leave it rather than about this one: *concurrent work belongs to a
867 /// task scope, and leaving the scope waits for or cancels its child
868 /// tasks.*
869 ///
870 /// `name` is what the source bound it to. It is carried because a
871 /// diagnostic quotes it — *task 2 of scope `requests`* — and by the time
872 /// a scope is a word there is nothing else left that knows.
873 ScopeEnter { dst: Slot, name: StrId },
874 /// Leave the scope in `scope` the way the body reached its end: wait for
875 /// every child, and say whether one of them failed in a way the
876 /// enclosing function has to pass on.
877 ///
878 /// `failed` is a `Bool`. When it is true, `error` holds the `Err`
879 /// payload of the first child whose value was one, at `layout` — and the
880 /// lowering wraps it in the enclosing function's own `Err` and returns
881 /// it, which is exactly what `?` would have done. A child that *raised*
882 /// is not that: a runtime error is not a value, so this instruction
883 /// fails with it and the two ways a child can end stay two things.
884 ///
885 /// A discriminated outcome rather than an instruction carrying control
886 /// flow, because where the failure goes is a fact about the function the
887 /// scope was written in — which `Err` to build, and what to return — and
888 /// the lowering is what holds those.
889 ScopeLeave {
890 scope: Slot,
891 failed: Slot,
892 error: Slot,
893 layout: LayoutId,
894 },
895 /// Cancel every child of the scope in `scope` and wait for it to stop.
896 ///
897 /// What an *early* exit from a scope's body reaches: a `return`, a `?`,
898 /// a `break` or a `continue` that leaves it. Leaving a scope waits for
899 /// or cancels its children whichever way it is left, so this is an
900 /// obligation on every exit path exactly as [`Inst::Clear`] is, and the
901 /// lowering emits one per open scope the jump leaves.
902 ///
903 /// It answers nothing. A scope being left early is already leaving with
904 /// something to say, and a child's failure discovered on the way out
905 /// would replace it with an unrelated one.
906 ScopeCancel { scope: Slot },
907 /// `dst = scope.spawn(closure)`, on a thread of its own.
908 ///
909 /// `answer` is the layout of the value the body produces, and it is here
910 /// because the answer needs somewhere to be *before* the thread exists:
911 /// the machine allocates an object of that width and records its address
912 /// in the scope's table, so the answer is an object in the run's one heap
913 /// and a root of this task from the moment it can hold anything. Handing
914 /// the words back through the thread instead would leave them in no
915 /// store the collector walks for as long as the join took.
916 ///
917 /// This returns once the thread exists and orders nothing else. ADR
918 /// 0008's amendment is explicit that whether the child has run an
919 /// instruction by the time the next one here does is the operating
920 /// system's answer.
921 Spawn {
922 dst: Slot,
923 scope: Slot,
924 closure: Slot,
925 answer: LayoutId,
926 },
927 /// `dst = await task`, for the words `answer` describes.
928 ///
929 /// Waits for the task's thread and answers the value its body produced.
930 /// A body runs at most once and is waited for at most once, so awaiting
931 /// the same handle twice answers the same value and repeats no effect.
932 Await {
933 dst: Slot,
934 task: Slot,
935 answer: LayoutId,
936 },
937 /// `task.cancel()`: ask the task to stop at its next safepoint.
938 ///
939 /// Asking is all it does. Whether the task stopped or had already
940 /// finished is known only where something waits for it, which is why
941 /// `TaskCancelled` is traced at the join and not here.
942 Cancel { task: Slot },
943 /// `dst = <a task already settled with the words at `src`>`.
944 ///
945 /// What a **call to an `async fn`** answers. The body ran at the call
946 /// site, on this task's stack, as [`Inst::Call`]; this is the handle the
947 /// call hands back, and there is no thread anywhere in it.
948 ///
949 /// That is the oracle's reading rather than an invention here.
950 /// `Interpreter::call_target` runs the body and wraps what it produced in
951 /// `crate::task::Task::settled`, and `crate::task::Task::settled`'s own
952 /// documentation says why: ADR 0008 gives a thread to `spawn`, which is
953 /// where the language says concurrency begins, so nothing may depend on
954 /// *when* an `async fn` body ran — only on the value an `await` produces.
955 /// A call that is never awaited has still run.
956 ///
957 /// So this task belongs to no scope and nothing joins it. It is
958 /// `position` zero of `crate::task::describe`, which is the case that
959 /// spelling exists for: *this task*, with no place in a spawn order to
960 /// name. The words are copied into an object of the same shape a
961 /// spawned task's answer goes into, because an `await` reads the two the
962 /// same way and a second arrangement would be a second thing to get
963 /// right.
964 Settled {
965 dst: Slot,
966 src: Slot,
967 answer: LayoutId,
968 },
969
970 // ---- cells ---------------------------------------------------------------
971 /// Take the [`crate::Shape::Shared`] cell in `cell`, waiting for whoever
972 /// holds it.
973 ///
974 /// ADR 0008 makes `lock` the whole of a `Shared`'s access: there is no
975 /// `get` and no `set`, so a read-modify-write cannot be written as two
976 /// operations that race. What that means here is an ordinary
977 /// [`Inst::CallClosure`] between this and [`Inst::SharedUnlock`], with the
978 /// address of the cell's value as the closure's argument — the same shape
979 /// `map` is lowered to, and for the same reason `docs/LINEAR_VM.md` gives:
980 /// **a builtin never calls back into Cove**. A builtin that ran the
981 /// closure itself would put a Rust frame under every Cove frame it made.
982 ///
983 /// So `lock` is *two* instructions rather than one that calls, and what
984 /// the second one costs is an obligation: **the release belongs to every
985 /// exit path**, exactly as [`Inst::Clear`] and [`Inst::ScopeCancel`] do.
986 /// The lowering emits it on the path that finished, and a runtime error —
987 /// which is not a jump the lowering can emit — is the machine's to answer,
988 /// once, for every cell the task was holding.
989 ///
990 /// A task that asks for a cell it already holds is refused rather than
991 /// made to wait, and that rule is untouched by
992 /// [ADR 0037](../../../docs/adr/0037-a-cycle-through-a-cell-is-an-ordinary-cycle.md):
993 /// waiting would be waiting for itself, and no collector can answer a live
994 /// lock state. What the ADR did remove is the *other* refusal — a closure
995 /// that leaves the cell holding a handle to itself is an ordinary
996 /// object-graph cycle now, collected when it becomes unreachable, so
997 /// nothing here inspects what the closure left.
998 SharedLock { cell: Slot },
999 /// Give the cell in `cell` back, publishing everything written while it
1000 /// was held.
1001 ///
1002 /// The lock word *is* the publication: it is taken with `Acquire` and
1003 /// released with `Release`, and every other word of the machine's memory
1004 /// is relaxed and is allowed to be. Acquiring a cell therefore makes
1005 /// visible not only its own words but every object the previous holder
1006 /// allocated and stored into them.
1007 SharedUnlock { cell: Slot },
1008
1009 // ---- failure ----------------------------------------------------------
1010 /// Fail the run with `message`.
1011 ///
1012 /// This is what an exhausted `match` and a failed `Unbox` reach. It is
1013 /// not a refusal to run the program: the program ran, and this is what
1014 /// it did.
1015 Trap { message: StrId },
1016
1017 /// Record that an assertion failed here, carrying the `String` in
1018 /// `message`.
1019 ///
1020 /// The one instruction that writes nothing a program can read. An
1021 /// assertion is lowered rather than performed — see this crate's
1022 /// `lower::assertions` — so by the time the failing arm runs, the
1023 /// `Err(Error("assertion failed: ..."))` is an ordinary value and the
1024 /// only thing left that the machine knows and the value does not is
1025 /// *where it was written*. A test runner points at the assertion the way
1026 /// every other error points at source, and this is how it is told.
1027 ///
1028 /// The span is the instruction's own, which is the assertion call's, so
1029 /// nothing has to be threaded through the program to carry it. The
1030 /// message is a slot rather than a [`StrId`] because `assertEqual`
1031 /// renders the two values it compared and that string is built at run
1032 /// time; a runner compares it against the `Err` it is holding, so that a
1033 /// later unrelated failure is not reported at this assertion.
1034 AssertFailed { message: Slot },
1035}