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 single unary math primitive.
20#[derive(Debug, Clone, Copy, PartialEq)]
21pub enum UnOp {
22 /// negate
23 Neg,
24 /// absolute value
25 Abs,
26 /// sine
27 Sin,
28 /// cosine
29 Cos,
30 /// tangent
31 Tan,
32 /// square root
33 Sqrt,
34 /// e^x
35 Exp,
36 /// natural log
37 Ln,
38 /// hyperbolic tangent
39 Tanh,
40}
41
42/// A single binary math primitive.
43#[derive(Debug, Clone, Copy, PartialEq)]
44pub enum BinArith {
45 /// +
46 Add,
47 /// -
48 Sub,
49 /// *
50 Mul,
51 /// /
52 Div,
53 /// %
54 Rem,
55 /// min
56 Min,
57 /// max
58 Max,
59}
60
61/// One IR instruction. `dst` is the scratch register it writes.
62#[derive(Debug, Clone, PartialEq)]
63pub enum Instr {
64 /// Load a constant.
65 Const {
66 /// Destination register.
67 dst: Reg,
68 /// Constant value to load.
69 value: f64,
70 },
71 /// Load the k-th program input for the current sample.
72 LoadInput {
73 /// Destination register.
74 dst: Reg,
75 /// Program input index.
76 index: usize,
77 },
78 /// Read a persistent state slot (its value from the *previous* sample).
79 ReadState {
80 /// Destination register.
81 dst: Reg,
82 /// State slot to read.
83 slot: StateSlot,
84 },
85 /// Read from a delay line: value `len` samples ago.
86 ReadDelay {
87 /// Destination register.
88 dst: Reg,
89 /// Delay line index.
90 line: usize,
91 },
92 /// Unary op.
93 Un {
94 /// Destination register.
95 dst: Reg,
96 /// Unary operation.
97 op: UnOp,
98 /// Source register.
99 src: Reg,
100 },
101 /// Binary op.
102 Bin {
103 /// Destination register.
104 dst: Reg,
105 /// Binary operation.
106 op: BinArith,
107 /// First operand register.
108 a: Reg,
109 /// Second operand register.
110 b: Reg,
111 },
112 /// Copy one register to another (wire).
113 Move {
114 /// Destination register.
115 dst: Reg,
116 /// Source register.
117 src: Reg,
118 },
119 /// Schedule a write of `src` into state slot at end of the sample.
120 WriteState {
121 /// State slot to write.
122 slot: StateSlot,
123 /// Source register.
124 src: Reg,
125 },
126 /// Schedule a push of `src` into a delay line at end of the sample.
127 WriteDelay {
128 /// Delay line index.
129 line: usize,
130 /// Source register.
131 src: Reg,
132 },
133 /// Call a per-sample built-in: `srcs` inputs → `dst`, instance index.
134 CallSample {
135 /// Destination register.
136 dst: Reg,
137 /// Source registers.
138 srcs: Vec<Reg>,
139 /// Index into [`Ir::builtins`].
140 instance: usize,
141 },
142 /// Call a whole-buffer built-in (1→1): `src` → `dst`, instance index.
143 CallBlock {
144 /// Destination register.
145 dst: Reg,
146 /// Source register.
147 src: Reg,
148 /// Index into [`Ir::builtins`].
149 instance: usize,
150 },
151 /// Read a named parameter slot. Value is constant within a block.
152 ReadParam {
153 /// Destination register.
154 dst: Reg,
155 /// Index into [`Ir::params`].
156 idx: usize,
157 },
158}
159
160/// Layout describing pre-allocated persistent storage.
161#[derive(Debug, Clone, Default, PartialEq)]
162pub struct StateLayout {
163 /// Number of scalar feedback state slots.
164 pub state_slots: usize,
165 /// Length (in samples) of each delay line.
166 pub delay_lens: Vec<usize>,
167}
168
169/// A resolved built-in call site: its name, folded constant params, and kind.
170/// Runtime instances are built from these by `RillProgram::new_with`.
171#[derive(Debug, Clone, PartialEq)]
172pub struct BuiltinInstance {
173 /// Registered built-in name.
174 pub name: String,
175 /// Folded constant params.
176 pub params: Vec<f64>,
177 /// Sample vs block.
178 pub kind: BuiltinKind,
179 /// (arg_position, param_idx) dynamic param drivers.
180 pub param_bindings: Vec<(usize, usize)>,
181}
182
183/// A named runtime parameter definition (a mutable control slot).
184#[derive(Debug, Clone, PartialEq)]
185pub struct ParamDef {
186 /// Parameter name.
187 pub name: String,
188 /// Initial/default value.
189 pub default: f64,
190 /// Minimum (clamp lower bound).
191 pub min: f64,
192 /// Maximum (clamp upper bound).
193 pub max: f64,
194}
195
196/// A complete lowered program.
197#[derive(Debug, Clone, PartialEq)]
198pub struct Ir {
199 /// Instructions in evaluation order.
200 pub instrs: Vec<Instr>,
201 /// Number of scratch registers required.
202 pub num_regs: usize,
203 /// The register holding the single program output at sample end.
204 pub output_reg: Reg,
205 /// Number of program inputs (0 or 1 for MVP).
206 pub num_inputs: usize,
207 /// Persistent state layout.
208 pub state: StateLayout,
209 /// Built-in call-site descriptors, indexed by the `instance` field of
210 /// [`Instr::CallSample`]/[`Instr::CallBlock`].
211 pub builtins: Vec<BuiltinInstance>,
212 /// Named parameter definitions, indexed by [`Instr::ReadParam::idx`].
213 pub params: Vec<ParamDef>,
214}