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