rill_lang/ir.rs
1//! Flat, register-machine intermediate representation.
2//!
3//! The IR computes the program's single output sample from its input sample(s)
4//! using a scratch register file (`Vec<f64>`, cleared per sample) plus a
5//! persistent state vector for feedback, `@` delays, and built-in calls.
6//! Instructions are in evaluation order; each writes exactly one register (SSA-like).
7//!
8//! The interpreter executes this per sample. The future Cranelift backend
9//! consumes the same structure.
10
11use crate::builtin::BuiltinKind;
12
13/// A register index into the per-sample scratch file.
14pub type Reg = usize;
15
16/// A slot index into the persistent state vector.
17pub type StateSlot = usize;
18
19/// A unique identifier for a probe point in the IR.
20#[cfg(feature = "debug")]
21pub type ProbeId = u32;
22
23/// A single unary math primitive.
24#[derive(Debug, Clone, Copy, PartialEq)]
25pub enum UnOp {
26 /// negate
27 Neg,
28 /// absolute value
29 Abs,
30 /// sine
31 Sin,
32 /// cosine
33 Cos,
34 /// tangent
35 Tan,
36 /// square root
37 Sqrt,
38 /// e^x
39 Exp,
40 /// natural log
41 Ln,
42 /// hyperbolic tangent
43 Tanh,
44}
45
46/// A single binary math primitive.
47#[derive(Debug, Clone, Copy, PartialEq)]
48pub enum BinArith {
49 /// +
50 Add,
51 /// -
52 Sub,
53 /// *
54 Mul,
55 /// /
56 Div,
57 /// %
58 Rem,
59 /// min
60 Min,
61 /// max
62 Max,
63}
64
65/// One IR instruction. `dst` is the scratch register it writes.
66#[derive(Debug, Clone, PartialEq)]
67pub enum Instr {
68 /// Load a constant.
69 Const {
70 /// Destination register.
71 dst: Reg,
72 /// Constant value to load.
73 value: f64,
74 },
75 /// Load the k-th program input for the current sample.
76 LoadInput {
77 /// Destination register.
78 dst: Reg,
79 /// Program input index.
80 index: usize,
81 },
82 /// Read a persistent state slot (its value from the *previous* sample).
83 ReadState {
84 /// Destination register.
85 dst: Reg,
86 /// State slot to read.
87 slot: StateSlot,
88 },
89 /// Read from a delay line: value `len` samples ago.
90 ReadDelay {
91 /// Destination register.
92 dst: Reg,
93 /// Delay line index.
94 line: usize,
95 },
96 /// Unary op.
97 Un {
98 /// Destination register.
99 dst: Reg,
100 /// Unary operation.
101 op: UnOp,
102 /// Source register.
103 src: Reg,
104 },
105 /// Binary op.
106 Bin {
107 /// Destination register.
108 dst: Reg,
109 /// Binary operation.
110 op: BinArith,
111 /// First operand register.
112 a: Reg,
113 /// Second operand register.
114 b: Reg,
115 },
116 /// Copy one register to another (wire).
117 Move {
118 /// Destination register.
119 dst: Reg,
120 /// Source register.
121 src: Reg,
122 },
123 /// Schedule a write of `src` into state slot at end of the sample.
124 WriteState {
125 /// State slot to write.
126 slot: StateSlot,
127 /// Source register.
128 src: Reg,
129 },
130 /// Schedule a push of `src` into a delay line at end of the sample.
131 WriteDelay {
132 /// Delay line index.
133 line: usize,
134 /// Source register.
135 src: Reg,
136 },
137 /// Call a per-sample built-in: `srcs` inputs → `dst`, instance index.
138 CallSample {
139 /// Destination register.
140 dst: Reg,
141 /// Source registers.
142 srcs: Vec<Reg>,
143 /// Index into [`Ir::builtins`].
144 instance: usize,
145 },
146 /// Call a whole-buffer built-in: `srcs` inputs → `dst`, instance index.
147 CallBlock {
148 /// Destination register (first output).
149 dst: Reg,
150 /// Source registers.
151 srcs: Vec<Reg>,
152 /// Index into [`Ir::builtins`].
153 instance: usize,
154 },
155 /// Read a named parameter slot. Value is constant within a block.
156 ReadParam {
157 /// Destination register.
158 dst: Reg,
159 /// Index into [`Ir::params`].
160 idx: usize,
161 },
162 /// Read an actor parameter slot (?name syntax). Semantically same as ReadParam
163 /// but carries distinct semantics for higher layers (actor param naming).
164 ReadActorParam {
165 /// Destination register.
166 dst: Reg,
167 /// Index into [`Ir::params`].
168 param_idx: usize,
169 },
170 /// A debug probe point that passes a signal through unchanged.
171 /// The runtime debug engine can latch this value for inspection.
172 #[cfg(feature = "debug")]
173 ProbePoint {
174 /// Unique probe identifier.
175 id: ProbeId,
176 /// Source register to copy from.
177 src: Reg,
178 /// Destination register to write to.
179 dst: Reg,
180 },
181}
182
183/// Layout describing pre-allocated persistent storage.
184#[derive(Debug, Clone, Default, PartialEq)]
185pub struct StateLayout {
186 /// Number of scalar feedback state slots.
187 pub state_slots: usize,
188 /// Length (in samples) of each delay line.
189 pub delay_lens: Vec<usize>,
190 /// Number of program outputs.
191 pub num_outputs: usize,
192}
193
194/// A resolved built-in call site: its name, folded constant params, and kind.
195/// Runtime instances are built from these by `RillProgram::new_with`.
196#[derive(Debug, Clone, PartialEq)]
197pub struct BuiltinInstance {
198 /// Registered built-in name.
199 pub name: String,
200 /// Folded constant params.
201 pub params: Vec<f64>,
202 /// Sample vs block.
203 pub kind: BuiltinKind,
204 /// Number of signal input channels.
205 pub signal_ins: usize,
206 /// Number of signal output channels.
207 pub signal_outs: usize,
208 /// (arg_position, param_idx) dynamic param drivers.
209 pub param_bindings: Vec<(usize, usize)>,
210}
211
212/// A named runtime parameter definition (a mutable control slot).
213#[derive(Debug, Clone, PartialEq)]
214pub struct ParamDef {
215 /// Parameter name.
216 pub name: String,
217 /// Initial/default value.
218 pub default: f64,
219 /// Minimum (clamp lower bound).
220 pub min: f64,
221 /// Maximum (clamp upper bound).
222 pub max: f64,
223}
224
225/// A complete lowered program.
226#[derive(Debug, Clone, PartialEq)]
227pub struct Ir {
228 /// Instructions in evaluation order.
229 pub instrs: Vec<Instr>,
230 /// Number of scratch registers required.
231 pub num_regs: usize,
232 /// The register holding the single program output at sample end.
233 pub output_reg: Reg,
234 /// Number of program inputs (0 or 1 for MVP).
235 pub num_inputs: usize,
236 /// Number of program outputs.
237 pub num_outputs: usize,
238 /// Persistent state layout.
239 pub state: StateLayout,
240 /// Built-in call-site descriptors, indexed by the `instance` field of
241 /// [`Instr::CallSample`]/[`Instr::CallBlock`].
242 pub builtins: Vec<BuiltinInstance>,
243 /// Named parameter definitions, indexed by [`Instr::ReadParam::idx`].
244 pub params: Vec<ParamDef>,
245}