Skip to main content

miden_assembly_syntax/ast/instruction/
print.rs

1use crate::{
2    DisplayHex,
3    ast::{Immediate, Instruction, InvocationTarget},
4    prettier::{Document, PrettyPrint},
5};
6
7impl PrettyPrint for Instruction {
8    fn render(&self) -> Document {
9        use crate::prettier::*;
10
11        if !self.has_textual_representation() {
12            return Document::Empty;
13        }
14
15        match self {
16            Self::Nop => const_text("nop"),
17            Self::Assert => const_text("assert"),
18            Self::AssertWithError(err_code) => flatten(
19                const_text("assert.err") + const_text("=") + text(format!("\"{err_code}\"")),
20            ),
21            Self::AssertEq => const_text("assert_eq"),
22            Self::AssertEqWithError(err_code) => flatten(
23                const_text("assert_eq.err") + const_text("=") + text(format!("\"{err_code}\"")),
24            ),
25            Self::AssertEqw => const_text("assert_eqw"),
26            Self::AssertEqwWithError(err_code) => flatten(
27                const_text("assert_eqw.err") + const_text("=") + text(format!("\"{err_code}\"")),
28            ),
29            Self::Assertz => const_text("assertz"),
30            Self::AssertzWithError(err_code) => flatten(
31                const_text("assertz.err") + const_text("=") + text(format!("\"{err_code}\"")),
32            ),
33            Self::Add => const_text("add"),
34            Self::AddImm(value) => inst_with_felt_imm("add", value),
35            Self::Sub => const_text("sub"),
36            Self::SubImm(value) => inst_with_felt_imm("sub", value),
37            Self::Mul => const_text("mul"),
38            Self::MulImm(value) => inst_with_felt_imm("mul", value),
39            Self::Div => const_text("div"),
40            Self::DivImm(value) => inst_with_felt_imm("div", value),
41            Self::Neg => const_text("neg"),
42            Self::ILog2 => const_text("ilog2"),
43            Self::Inv => const_text("inv"),
44            Self::Incr => const_text("add.1"),
45            Self::Pow2 => const_text("pow2"),
46            Self::Exp => const_text("exp"),
47            Self::ExpImm(value) => inst_with_felt_imm("exp", value),
48            Self::ExpBitLength(value) => text(format!("exp.u{value}")),
49            Self::Not => const_text("not"),
50            Self::And => const_text("and"),
51            Self::Or => const_text("or"),
52            Self::Xor => const_text("xor"),
53            Self::Eq => const_text("eq"),
54            Self::EqImm(value) => inst_with_felt_imm("eq", value),
55            Self::Neq => const_text("neq"),
56            Self::NeqImm(value) => inst_with_felt_imm("neq", value),
57            Self::Eqw => const_text("eqw"),
58            Self::Lt => const_text("lt"),
59            Self::Lte => const_text("lte"),
60            Self::Gt => const_text("gt"),
61            Self::Gte => const_text("gte"),
62            Self::IsOdd => const_text("is_odd"),
63
64            // ----- ext2 operations --------------------------------------------------------------
65            Self::Ext2Add => const_text("ext2add"),
66            Self::Ext2Sub => const_text("ext2sub"),
67            Self::Ext2Mul => const_text("ext2mul"),
68            Self::Ext2Div => const_text("ext2div"),
69            Self::Ext2Neg => const_text("ext2neg"),
70            Self::Ext2Inv => const_text("ext2inv"),
71
72            // ----- u32 manipulation -------------------------------------------------------------
73            Self::U32Test => const_text("u32test"),
74            Self::U32TestW => const_text("u32testw"),
75            Self::U32Assert => const_text("u32assert"),
76            Self::U32AssertWithError(err_code) => flatten(
77                const_text("u32assert.err") + const_text("=") + text(format!("\"{err_code}\"")),
78            ),
79            Self::U32Assert2 => const_text("u32assert2"),
80            Self::U32Assert2WithError(err_code) => flatten(
81                const_text("u32assert2.err") + const_text("=") + text(format!("\"{err_code}\"")),
82            ),
83            Self::U32AssertW => const_text("u32assertw"),
84            Self::U32AssertWWithError(err_code) => flatten(
85                const_text("u32assertw.err") + const_text("=") + text(format!("\"{err_code}\"")),
86            ),
87            Self::U32Split => const_text("u32split"),
88            Self::U32Cast => const_text("u32cast"),
89            Self::U32WrappingAdd => const_text("u32wrapping_add"),
90            Self::U32WrappingAddImm(value) => inst_with_imm("u32wrapping_add", value),
91            Self::U32OverflowingAdd => const_text("u32overflowing_add"),
92            Self::U32OverflowingAddImm(value) => inst_with_imm("u32overflowing_add", value),
93            Self::U32WideningAdd => const_text("u32widening_add"),
94            Self::U32WideningAddImm(value) => inst_with_imm("u32widening_add", value),
95            Self::U32OverflowingAdd3 => const_text("u32overflowing_add3"),
96            Self::U32WideningAdd3 => const_text("u32widening_add3"),
97            Self::U32WrappingAdd3 => const_text("u32wrapping_add3"),
98            Self::U32WrappingSub => const_text("u32wrapping_sub"),
99            Self::U32WrappingSubImm(value) => inst_with_imm("u32wrapping_sub", value),
100            Self::U32OverflowingSub => const_text("u32overflowing_sub"),
101            Self::U32OverflowingSubImm(value) => inst_with_imm("u32overflowing_sub", value),
102            Self::U32WrappingMul => const_text("u32wrapping_mul"),
103            Self::U32WrappingMulImm(value) => inst_with_imm("u32wrapping_mul", value),
104            Self::U32WideningMul => const_text("u32widening_mul"),
105            Self::U32WideningMulImm(value) => inst_with_imm("u32widening_mul", value),
106            Self::U32WideningMadd => const_text("u32widening_madd"),
107            Self::U32WrappingMadd => const_text("u32wrapping_madd"),
108            Self::U32Div => const_text("u32div"),
109            Self::U32DivImm(value) => inst_with_imm("u32div", value),
110            Self::U32Mod => const_text("u32mod"),
111            Self::U32ModImm(value) => inst_with_imm("u32mod", value),
112            Self::U32DivMod => const_text("u32divmod"),
113            Self::U32DivModImm(value) => inst_with_imm("u32divmod", value),
114            Self::U32And => const_text("u32and"),
115            Self::U32Or => const_text("u32or"),
116            Self::U32Xor => const_text("u32xor"),
117            Self::U32Not => const_text("u32not"),
118            Self::U32Shr => const_text("u32shr"),
119            Self::U32ShrImm(value) => inst_with_imm("u32shr", value),
120            Self::U32Shl => const_text("u32shl"),
121            Self::U32ShlImm(value) => inst_with_imm("u32shl", value),
122            Self::U32Rotr => const_text("u32rotr"),
123            Self::U32RotrImm(value) => inst_with_imm("u32rotr", value),
124            Self::U32Rotl => const_text("u32rotl"),
125            Self::U32RotlImm(value) => inst_with_imm("u32rotl", value),
126            Self::U32Popcnt => const_text("u32popcnt"),
127            Self::U32Clz => const_text("u32clz"),
128            Self::U32Ctz => const_text("u32ctz"),
129            Self::U32Clo => const_text("u32clo"),
130            Self::U32Cto => const_text("u32cto"),
131            Self::U32Lt => const_text("u32lt"),
132            Self::U32Lte => const_text("u32lte"),
133            Self::U32Gt => const_text("u32gt"),
134            Self::U32Gte => const_text("u32gte"),
135            Self::U32Min => const_text("u32min"),
136            Self::U32Max => const_text("u32max"),
137
138            // ----- stack manipulation -----------------------------------------------------------
139            Self::Drop => const_text("drop"),
140            Self::DropW => const_text("dropw"),
141            Self::PadW => const_text("padw"),
142            Self::Dup0 => const_text("dup.0"),
143            Self::Dup1 => const_text("dup.1"),
144            Self::Dup2 => const_text("dup.2"),
145            Self::Dup3 => const_text("dup.3"),
146            Self::Dup4 => const_text("dup.4"),
147            Self::Dup5 => const_text("dup.5"),
148            Self::Dup6 => const_text("dup.6"),
149            Self::Dup7 => const_text("dup.7"),
150            Self::Dup8 => const_text("dup.8"),
151            Self::Dup9 => const_text("dup.9"),
152            Self::Dup10 => const_text("dup.10"),
153            Self::Dup11 => const_text("dup.11"),
154            Self::Dup12 => const_text("dup.12"),
155            Self::Dup13 => const_text("dup.13"),
156            Self::Dup14 => const_text("dup.14"),
157            Self::Dup15 => const_text("dup.15"),
158            Self::DupW0 => const_text("dupw.0"),
159            Self::DupW1 => const_text("dupw.1"),
160            Self::DupW2 => const_text("dupw.2"),
161            Self::DupW3 => const_text("dupw.3"),
162            Self::Swap1 => const_text("swap.1"),
163            Self::Swap2 => const_text("swap.2"),
164            Self::Swap3 => const_text("swap.3"),
165            Self::Swap4 => const_text("swap.4"),
166            Self::Swap5 => const_text("swap.5"),
167            Self::Swap6 => const_text("swap.6"),
168            Self::Swap7 => const_text("swap.7"),
169            Self::Swap8 => const_text("swap.8"),
170            Self::Swap9 => const_text("swap.9"),
171            Self::Swap10 => const_text("swap.10"),
172            Self::Swap11 => const_text("swap.11"),
173            Self::Swap12 => const_text("swap.12"),
174            Self::Swap13 => const_text("swap.13"),
175            Self::Swap14 => const_text("swap.14"),
176            Self::Swap15 => const_text("swap.15"),
177            Self::SwapW1 => const_text("swapw.1"),
178            Self::SwapW2 => const_text("swapw.2"),
179            Self::SwapW3 => const_text("swapw.3"),
180            Self::SwapDw => const_text("swapdw"),
181            Self::MovUp2 => const_text("movup.2"),
182            Self::MovUp3 => const_text("movup.3"),
183            Self::MovUp4 => const_text("movup.4"),
184            Self::MovUp5 => const_text("movup.5"),
185            Self::MovUp6 => const_text("movup.6"),
186            Self::MovUp7 => const_text("movup.7"),
187            Self::MovUp8 => const_text("movup.8"),
188            Self::MovUp9 => const_text("movup.9"),
189            Self::MovUp10 => const_text("movup.10"),
190            Self::MovUp11 => const_text("movup.11"),
191            Self::MovUp12 => const_text("movup.12"),
192            Self::MovUp13 => const_text("movup.13"),
193            Self::MovUp14 => const_text("movup.14"),
194            Self::MovUp15 => const_text("movup.15"),
195            Self::MovUpW2 => const_text("movupw.2"),
196            Self::MovUpW3 => const_text("movupw.3"),
197            Self::MovDn2 => const_text("movdn.2"),
198            Self::MovDn3 => const_text("movdn.3"),
199            Self::MovDn4 => const_text("movdn.4"),
200            Self::MovDn5 => const_text("movdn.5"),
201            Self::MovDn6 => const_text("movdn.6"),
202            Self::MovDn7 => const_text("movdn.7"),
203            Self::MovDn8 => const_text("movdn.8"),
204            Self::MovDn9 => const_text("movdn.9"),
205            Self::MovDn10 => const_text("movdn.10"),
206            Self::MovDn11 => const_text("movdn.11"),
207            Self::MovDn12 => const_text("movdn.12"),
208            Self::MovDn13 => const_text("movdn.13"),
209            Self::MovDn14 => const_text("movdn.14"),
210            Self::MovDn15 => const_text("movdn.15"),
211            Self::MovDnW2 => const_text("movdnw.2"),
212            Self::MovDnW3 => const_text("movdnw.3"),
213            Self::Reversew => const_text("reversew"),
214            Self::Reversedw => const_text("reversedw"),
215            Self::CSwap => const_text("cswap"),
216            Self::CSwapW => const_text("cswapw"),
217            Self::CDrop => const_text("cdrop"),
218            Self::CDropW => const_text("cdropw"),
219
220            // ----- input / output operations ----------------------------------------------------
221            Self::Push(value) => inst_with_imm("push", value),
222            Self::PushSlice(value, range) => flatten(
223                const_text("push.")
224                    + value.render()
225                    + const_text("[")
226                    + display(range.start)
227                    + const_text("..")
228                    + display(range.end)
229                    + const_text("]"),
230            ),
231            Self::PushFeltList(values) => inst_with_pretty_felt_params("push", values),
232            Self::Locaddr(value) => inst_with_imm("locaddr", value),
233            Self::Sdepth => const_text("sdepth"),
234            Self::Caller => const_text("caller"),
235            Self::Clk => const_text("clk"),
236
237            Self::MemLoad => const_text("mem_load"),
238            Self::MemLoadImm(value) => inst_with_imm("mem_load", value),
239            Self::MemLoadWBe => const_text("mem_loadw_be"),
240            Self::MemLoadWBeImm(value) => inst_with_imm("mem_loadw_be", value),
241            Self::MemLoadWLe => const_text("mem_loadw_le"),
242            Self::MemLoadWLeImm(value) => inst_with_imm("mem_loadw_le", value),
243            Self::LocLoad(value) => inst_with_imm("loc_load", value),
244            Self::LocLoadWBe(value) => inst_with_imm("loc_loadw_be", value),
245            Self::LocLoadWLe(value) => inst_with_imm("loc_loadw_le", value),
246
247            Self::MemStore => const_text("mem_store"),
248            Self::MemStoreImm(value) => inst_with_imm("mem_store", value),
249            Self::MemStoreWBe => const_text("mem_storew_be"),
250            Self::MemStoreWBeImm(value) => inst_with_imm("mem_storew_be", value),
251            Self::MemStoreWLe => const_text("mem_storew_le"),
252            Self::MemStoreWLeImm(value) => inst_with_imm("mem_storew_le", value),
253            Self::LocStore(value) => inst_with_imm("loc_store", value),
254            Self::LocStoreWBe(value) => inst_with_imm("loc_storew_be", value),
255            Self::LocStoreWLe(value) => inst_with_imm("loc_storew_le", value),
256
257            Self::MemStream => const_text("mem_stream"),
258            Self::AdvPipe => const_text("adv_pipe"),
259
260            Self::AdvPush => const_text("adv_push"),
261            Self::AdvPushW => const_text("adv_pushw"),
262            Self::AdvLoadW => const_text("adv_loadw"),
263
264            Self::SysEvent(sys_event) => inst_with_imm("adv", sys_event),
265
266            // ----- cryptographic operations -----------------------------------------------------
267            Self::Hash => const_text("hash"),
268            Self::HMerge => const_text("hmerge"),
269            Self::HPerm => const_text("hperm"),
270            Self::MTreeGet => const_text("mtree_get"),
271            Self::MTreeSet => const_text("mtree_set"),
272            Self::MTreeMerge => const_text("mtree_merge"),
273            Self::MTreeVerify => const_text("mtree_verify"),
274            Self::MTreeVerifyWithError(err_code) => flatten(
275                const_text("mtree_verify.err") + const_text("=") + text(format!("\"{err_code}\"")),
276            ),
277            Self::CryptoStream => const_text("crypto_stream"),
278
279            // ----- STARK proof verification -----------------------------------------------------
280            Self::FriExt2Fold4 => const_text("fri_ext2fold4"),
281            Self::HornerBase => const_text("horner_eval_base"),
282            Self::HornerExt => const_text("horner_eval_ext"),
283            Self::EvalCircuit => const_text("eval_circuit"),
284            Self::LogPrecompile => const_text("log_precompile"),
285
286            // ----- exec / call ------------------------------------------------------------------
287            Self::Exec(InvocationTarget::MastRoot(root)) => flatten(
288                const_text("exec")
289                    + const_text(".")
290                    + text(format!("{:#x}", DisplayHex(root.as_bytes().as_slice()))),
291            ),
292            Self::Exec(InvocationTarget::Symbol(name)) => {
293                flatten(const_text("exec") + const_text(".") + text(name))
294            },
295            Self::Exec(InvocationTarget::Path(path)) => {
296                const_text("exec") + const_text(".") + display(path)
297            },
298            Self::Call(InvocationTarget::MastRoot(root)) => {
299                const_text("call")
300                    + const_text(".")
301                    + text(format!("{:#x}", DisplayHex(root.as_bytes().as_slice())))
302            },
303            Self::Call(InvocationTarget::Symbol(name)) => {
304                flatten(const_text("call") + const_text(".") + text(name))
305            },
306            Self::Call(InvocationTarget::Path(path)) => {
307                const_text("call") + const_text(".") + display(path)
308            },
309            Self::SysCall(InvocationTarget::MastRoot(root)) => {
310                const_text("syscall")
311                    + const_text(".")
312                    + text(format!("{:#x}", DisplayHex(root.as_bytes().as_slice())))
313            },
314            Self::SysCall(InvocationTarget::Symbol(name)) => {
315                flatten(const_text("syscall") + const_text(".") + text(format!("{name}")))
316            },
317            Self::SysCall(InvocationTarget::Path(path)) => {
318                const_text("syscall") + const_text(".") + display(path)
319            },
320            Self::DynExec => const_text("dynexec"),
321            Self::DynCall => const_text("dyncall"),
322            Self::ProcRef(InvocationTarget::MastRoot(_)) => {
323                panic!("invalid procref instruction: expected name not MAST root")
324            },
325            Self::ProcRef(InvocationTarget::Symbol(name)) => {
326                flatten(const_text("procref") + const_text(".") + text(name))
327            },
328            Self::ProcRef(InvocationTarget::Path(path)) => {
329                flatten(const_text("procref") + const_text(".") + display(path))
330            },
331
332            // ----- debug decorators -------------------------------------------------------------
333            Self::Debug(options) => inst_with_imm("debug", options),
334
335            // ----- event decorators -------------------------------------------------------------
336            Self::Emit => const_text("emit"),
337            Self::EmitImm(value) => inst_with_felt_imm("emit", value),
338            Self::Trace(value) => inst_with_imm("trace", value),
339
340            // Handled by the early return for !has_textual_representation()
341            Self::DebugVar(_) => unreachable!(),
342        }
343    }
344}
345
346fn inst_with_imm(name: &'static str, imm: &dyn PrettyPrint) -> Document {
347    use crate::prettier::*;
348
349    let imm = imm.render();
350
351    flatten(const_text(name) + const_text(".") + imm)
352}
353
354fn inst_with_felt_imm(name: &'static str, imm: &Immediate<crate::Felt>) -> Document {
355    use crate::prettier::*;
356
357    let value = match imm {
358        Immediate::Value(value) => display(*value),
359        Immediate::Constant(name) => text(name),
360    };
361
362    flatten(const_text(name) + const_text(".") + value)
363}
364
365fn inst_with_pretty_felt_params(inst: &'static str, params: &[crate::Felt]) -> Document {
366    use crate::prettier::*;
367
368    let single_line = text(inst)
369        + const_text(".")
370        + params
371            .iter()
372            .copied()
373            .map(display)
374            .reduce(|acc, doc| acc + const_text(".") + doc)
375            .unwrap_or_default();
376
377    let multi_line = params
378        .iter()
379        .copied()
380        .map(|v| text(inst) + const_text(".") + display(v))
381        .reduce(|acc, doc| acc + nl() + doc)
382        .unwrap_or_default();
383    single_line | multi_line
384}
385
386// TESTS
387// ================================================================================================
388
389#[cfg(test)]
390mod tests {
391    use miden_core::crypto::hash::Poseidon2;
392    use miden_debug_types::Span;
393
394    use crate::{
395        Felt,
396        ast::*,
397        parser::{IntValue, PushValue},
398    };
399
400    #[test]
401    fn test_instruction_display() {
402        let instruction = format!("{}", Instruction::Assert);
403        assert_eq!("assert", instruction);
404
405        let instruction = format!("{}", Instruction::Add);
406        assert_eq!("add", instruction);
407
408        let instruction = format!("{}", Instruction::AddImm(Felt::new_unchecked(5).into()));
409        assert_eq!("add.5", instruction);
410
411        let instruction = format!("{}", Instruction::ExpBitLength(32));
412        assert_eq!("exp.u32", instruction);
413
414        let instruction = format!(
415            "{}",
416            Instruction::PushFeltList(vec![
417                Felt::new_unchecked(3),
418                Felt::new_unchecked(4),
419                Felt::new_unchecked(8),
420                Felt::new_unchecked(9)
421            ])
422        );
423        assert_eq!("push.3.4.8.9", instruction);
424        let instruction = format!(
425            "{}",
426            Instruction::Push(Immediate::Value(Span::unknown(PushValue::Int(IntValue::U8(3)))))
427        );
428        assert_eq!("push.3", instruction);
429
430        let digest = Poseidon2::hash(b"miden::core::math::u64::add");
431        let target = InvocationTarget::MastRoot(Span::unknown(digest));
432        let instruction = format!("{}", Instruction::Exec(target));
433        assert_eq!(
434            "exec.0x23796e2a4ee91a63c116e11dbbc2ea599461766d1fb7b6599ca387cab2c3e64c",
435            instruction
436        );
437    }
438}