Skip to main content

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}