1use std::collections::BTreeMap;
2use std::collections::BTreeSet;
3use std::fmt::Write as _;
4
5use crate::cfg::BlockTerm;
6use crate::decode::{Instruction, Op};
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub enum UnOp {
10 Neg,
11}
12
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub enum BinOp {
15 Add,
16 Sub,
17 Mul,
18 Div,
19 Mod,
20 BitAnd,
21 And,
22 Or,
23 Eq,
24 Ne,
25 Gt,
26 Ge,
27 Lt,
28 Le,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq)]
32pub enum Expr {
33 Nil,
34 Bool(bool),
35 Int(i64),
36 Float(String),
37 Str(String),
38 Var(String),
39 Unary(UnOp, Box<Expr>),
40 Binary(BinOp, Box<Expr>, Box<Expr>),
41 Index(Box<Expr>, Box<Expr>),
42}
43
44impl Expr {
45 pub fn var(name: impl Into<String>) -> Self {
46 Expr::Var(name.into())
47 }
48
49 pub fn stack_var(idx: usize) -> Self {
50 Expr::Var(format!("S{}", idx))
51 }
52
53 pub fn global_var() -> Self {
54 Expr::Var("G".to_string())
55 }
56
57 pub fn global_table_var() -> Self {
58 Expr::Var("GT".to_string())
59 }
60
61 pub fn local_table_var() -> Self {
62 Expr::Var("LT".to_string())
63 }
64
65 pub fn index(base: Expr, idx: Expr) -> Self {
66 Expr::Index(Box::new(base), Box::new(idx))
67 }
68
69 pub fn unary(op: UnOp, a: Expr) -> Self {
70 match (&op, &a) {
71 (UnOp::Neg, Expr::Int(v)) => Expr::Int(-v),
72 _ => Expr::Unary(op, Box::new(a)),
73 }
74 }
75
76 pub fn binary(op: BinOp, a: Expr, b: Expr) -> Self {
77 match (&op, &a, &b) {
79 (BinOp::Add, Expr::Int(x), Expr::Int(y)) => return Expr::Int(x + y),
80 (BinOp::Sub, Expr::Int(x), Expr::Int(y)) => return Expr::Int(x - y),
81 (BinOp::Mul, Expr::Int(x), Expr::Int(y)) => return Expr::Int(x * y),
82 (BinOp::Eq, Expr::Int(x), Expr::Int(y)) => return Expr::Bool(x == y),
83 (BinOp::Ne, Expr::Int(x), Expr::Int(y)) => return Expr::Bool(x != y),
84 (BinOp::Gt, Expr::Int(x), Expr::Int(y)) => return Expr::Bool(x > y),
85 (BinOp::Ge, Expr::Int(x), Expr::Int(y)) => return Expr::Bool(x >= y),
86 (BinOp::Lt, Expr::Int(x), Expr::Int(y)) => return Expr::Bool(x < y),
87 (BinOp::Le, Expr::Int(x), Expr::Int(y)) => return Expr::Bool(x <= y),
88
89 (BinOp::Eq, Expr::Nil, Expr::Nil) => return Expr::Bool(true),
91 (BinOp::Ne, Expr::Nil, Expr::Nil) => return Expr::Bool(false),
92 (BinOp::Eq, Expr::Nil, _) | (BinOp::Eq, _, Expr::Nil) => {
93 if matches!(a, Expr::Nil) {
95 if matches!(
96 b,
97 Expr::Bool(_) | Expr::Int(_) | Expr::Float(_) | Expr::Str(_)
98 ) {
99 return Expr::Bool(false);
100 }
101 }
102 if matches!(b, Expr::Nil) {
103 if matches!(
104 a,
105 Expr::Bool(_) | Expr::Int(_) | Expr::Float(_) | Expr::Str(_)
106 ) {
107 return Expr::Bool(false);
108 }
109 }
110 }
111 (BinOp::Ne, Expr::Nil, _) | (BinOp::Ne, _, Expr::Nil) => {
112 if matches!(a, Expr::Nil) {
113 if matches!(
114 b,
115 Expr::Bool(_) | Expr::Int(_) | Expr::Float(_) | Expr::Str(_)
116 ) {
117 return Expr::Bool(true);
118 }
119 }
120 if matches!(b, Expr::Nil) {
121 if matches!(
122 a,
123 Expr::Bool(_) | Expr::Int(_) | Expr::Float(_) | Expr::Str(_)
124 ) {
125 return Expr::Bool(true);
126 }
127 }
128 }
129
130 (BinOp::Add, x, Expr::Int(0)) => return x.clone(),
132 (BinOp::Add, Expr::Int(0), x) => return x.clone(),
133 (BinOp::Sub, x, Expr::Int(0)) => return x.clone(),
134 (BinOp::Mul, x, Expr::Int(1)) => return x.clone(),
135 (BinOp::Mul, Expr::Int(1), x) => return x.clone(),
136
137 _ => {}
138 }
139 Expr::Binary(op, Box::new(a), Box::new(b))
140 }
141
142 fn precedence(&self) -> u8 {
143 match self {
144 Expr::Nil
145 | Expr::Bool(_)
146 | Expr::Int(_)
147 | Expr::Float(_)
148 | Expr::Str(_)
149 | Expr::Var(_)
150 | Expr::Index(_, _) => 100,
151 Expr::Unary(_, _) => 90,
152 Expr::Binary(op, _, _) => match op {
153 BinOp::Mul | BinOp::Div | BinOp::Mod => 80,
154 BinOp::Add | BinOp::Sub => 70,
155 BinOp::BitAnd => 65,
156 BinOp::Eq | BinOp::Ne | BinOp::Gt | BinOp::Ge | BinOp::Lt | BinOp::Le => 50,
157 BinOp::And => 40,
158 BinOp::Or => 30,
159 },
160 }
161 }
162
163 fn mark_stack_var(name: &str, used_s: &mut BTreeSet<usize>) {
164 if let Some(rest) = name.strip_prefix('S') {
165 if !rest.is_empty() && rest.bytes().all(|c| c.is_ascii_digit()) {
166 if let Ok(v) = rest.parse::<usize>() {
167 used_s.insert(v);
168 }
169 }
170 }
171 }
172
173 pub fn render(&self, used_s: &mut BTreeSet<usize>, parent_prec: u8) -> String {
174 let my_prec = self.precedence();
175 let mut s = match self {
176 Expr::Nil => "nil".to_string(),
177 Expr::Bool(v) => {
178 if *v {
179 "true".to_string()
180 } else {
181 "false".to_string()
182 }
183 }
184 Expr::Int(v) => v.to_string(),
185 Expr::Float(v) => v.clone(),
186 Expr::Str(v) => {
187 let lit = v.replace('\\', "\\\\").replace('"', "\\\"");
188 format!("\"{}\"", lit)
189 }
190 Expr::Var(v) => {
191 Self::mark_stack_var(v, used_s);
192 v.clone()
193 }
194 Expr::Index(base, idx) => {
195 let b = base.render(used_s, 100);
196 let i = idx.render(used_s, 0);
197 format!("{}[{}]", b, i)
198 }
199 Expr::Unary(UnOp::Neg, a) => {
200 let aa = a.render(used_s, my_prec);
201 format!("-{}", aa)
202 }
203 Expr::Binary(op, a, b) => {
204 let aa = a.render(used_s, my_prec);
205 let bb = b.render(used_s, my_prec + 1);
206 let op_s = match op {
207 BinOp::Add => "+",
208 BinOp::Sub => "-",
209 BinOp::Mul => "*",
210 BinOp::Div => "/",
211 BinOp::Mod => "%",
212 BinOp::BitAnd => "&",
213 BinOp::And => "and",
214 BinOp::Or => "or",
215 BinOp::Eq => "==",
216 BinOp::Ne => "~=",
217 BinOp::Gt => ">",
218 BinOp::Ge => ">=",
219 BinOp::Lt => "<",
220 BinOp::Le => "<=",
221 };
222 if matches!(op, BinOp::And | BinOp::Or) {
223 format!("({}) {} ({})", aa, op_s, bb)
224 } else {
225 format!("{} {} {}", aa, op_s, bb)
226 }
227 }
228 };
229
230 if my_prec < parent_prec {
231 s = format!("({})", s);
232 }
233 s
234 }
235
236 pub fn const_eq_zero(&self) -> Option<bool> {
237 match self {
238 Expr::Int(v) => Some(*v == 0),
239 Expr::Float(s) => s.parse::<f64>().ok().map(|v| v == 0.0),
241 _ => None,
242 }
243 }
244}
245
246fn global_name(idx: u16, non_volatile_count: u16, volatile_count: u16) -> String {
247 if idx < non_volatile_count {
248 return format!("g{}", idx);
249 }
250
251 let vbase = non_volatile_count;
252 let vlimit = non_volatile_count.saturating_add(volatile_count);
253 if idx >= vbase && idx < vlimit {
254 return format!("vg{}", idx - vbase);
255 }
256
257 format!("G[{}]", idx)
258}
259
260pub struct BlockEmitter<'a> {
261 indent: &'a str,
262 func_args: u8,
263 callee_args: &'a BTreeMap<u32, u8>,
264 used_s: &'a mut BTreeSet<usize>,
265 out: String,
266 stack: Vec<Expr>,
267 non_volatile_global_count: u16,
268 volatile_global_count: u16,
269}
270
271impl<'a> BlockEmitter<'a> {
272 pub fn new(
273 indent: &'a str,
274 func_args: u8,
275 callee_args: &'a BTreeMap<u32, u8>,
276 used_s: &'a mut BTreeSet<usize>,
277 non_volatile_global_count: u16,
278 volatile_global_count: u16,
279 ) -> Self {
280 BlockEmitter {
281 indent,
282 func_args,
283 callee_args,
284 used_s,
285 out: String::new(),
286 stack: Vec::new(),
287 non_volatile_global_count,
288 volatile_global_count,
289 }
290 }
291
292 pub fn init_stack(&mut self, depth: usize) {
293 self.stack.clear();
294 for i in 0..depth {
295 self.stack.push(Expr::stack_var(i));
296 }
297 }
298
299 pub fn take_output(self) -> String {
300 self.out
301 }
302
303 fn emit_line(&mut self, line: &str) {
304 let _ = writeln!(&mut self.out, "{}{}", self.indent, line);
305 }
306
307 fn pop(&mut self) -> Expr {
308 self.stack.pop().unwrap_or(Expr::Nil)
309 }
310
311 fn push(&mut self, e: Expr) {
312 self.stack.push(e);
313 }
314
315 fn stack_slot_get(&self, idx: i8) -> String {
316 if idx < 0 {
318 let abs = (-idx) as u8 - 2;
319 if abs <= self.func_args {
320 let a = (self.func_args - abs) as usize;
321 return format!("a{}", a);
322 }
323 return format!("a_{}", idx);
324 }
325
326 let u = idx as u8;
327 if u < self.func_args {
328 format!("a{}", u as usize)
329 } else {
330 let l = (u - self.func_args) as usize;
331 format!("l{}", l)
332 }
333 }
334
335 fn materialize_all_stack_slots(&mut self) {
336 for i in 0..self.stack.len() {
338 let want = Expr::stack_var(i);
339 if self.stack[i] != want {
340 let rhs = self.stack[i].render(self.used_s, 0);
341 self.used_s.insert(i);
342 self.emit_line(&format!("S{} = {}", i, rhs));
343 self.stack[i] = Expr::stack_var(i);
344 } else {
345 }
347 }
348 }
349
350 pub fn emit_inst(&mut self, inst: &Instruction) {
351 match &inst.op {
352 Op::Nop | Op::InitStack { .. } => {}
353
354 Op::PushNil => self.push(Expr::Nil),
355 Op::PushTrue => self.push(Expr::Bool(true)),
356 Op::PushI8(v) => self.push(Expr::Int(*v as i64)),
357 Op::PushI16(v) => self.push(Expr::Int(*v as i64)),
358 Op::PushI32(v) => self.push(Expr::Int(*v as i64)),
359 Op::PushF32(v) => self.push(Expr::Float(format!("{}", v))),
360 Op::PushString(s) => self.push(Expr::Str(s.clone())),
361
362 Op::PushTop => {
363 if let Some(top) = self.stack.last().cloned() {
364 self.push(top);
365 } else {
366 self.push(Expr::Nil);
367 }
368 }
369 Op::PushReturn => self.push(Expr::var("__ret")),
370
371 Op::PushGlobal(idx) => {
372 self.push(Expr::var(global_name(
373 *idx,
374 self.non_volatile_global_count,
375 self.volatile_global_count,
376 )));
377 }
378 Op::PopGlobal(idx) => {
379 let v = self.pop();
380 let rhs = v.render(self.used_s, 0);
381 self.emit_line(&format!(
382 "{} = {}",
383 global_name(
384 *idx,
385 self.non_volatile_global_count,
386 self.volatile_global_count,
387 ),
388 rhs
389 ));
390 }
391
392 Op::PushStack(idx) => {
393 let v = self.stack_slot_get(*idx);
394 self.push(Expr::var(v));
395 }
396 Op::PopStack(idx) => {
397 let rhs_expr = self.pop();
398 let rhs = rhs_expr.render(self.used_s, 0);
399 let lhs = self.stack_slot_get(*idx);
400 self.emit_line(&format!("{} = {}", lhs, rhs));
401 }
402
403 Op::PushGlobalTable(idx) => {
404 if let Some(last) = self.stack.pop() {
406 let base = Expr::index(Expr::global_table_var(), Expr::Int(*idx as i64));
407 let e = Expr::index(base, last);
408 self.stack.push(e);
409 } else {
410 self.stack.push(Expr::Nil);
411 }
412 }
413 Op::PopGlobalTable(idx) => {
414 let v = self.pop();
416 let k = self.pop();
417 let base = Expr::index(Expr::global_table_var(), Expr::Int(*idx as i64));
418 let lhs = Expr::index(base, k).render(self.used_s, 0);
419 let rhs = v.render(self.used_s, 0);
420 self.emit_line(&format!("{} = {}", lhs, rhs));
421 }
422
423 Op::PushLocalTable(idx) => {
424 if let Some(last) = self.stack.pop() {
425 let base = Expr::index(Expr::local_table_var(), Expr::Int(*idx as i64));
426 let e = Expr::index(base, last);
427 self.stack.push(e);
428 } else {
429 self.stack.push(Expr::Nil);
430 }
431 }
432 Op::PopLocalTable(idx) => {
433 let v = self.pop();
434 let k = self.pop();
435 let base = Expr::index(Expr::local_table_var(), Expr::Int(*idx as i64));
436 let lhs = Expr::index(base, k).render(self.used_s, 0);
437 let rhs = v.render(self.used_s, 0);
438 self.emit_line(&format!("{} = {}", lhs, rhs));
439 }
440
441 Op::Neg => {
442 let a = self.pop();
443 self.push(Expr::unary(UnOp::Neg, a));
444 }
445
446 Op::Add
447 | Op::Sub
448 | Op::Mul
449 | Op::Div
450 | Op::Mod
451 | Op::BitTest
452 | Op::And
453 | Op::Or
454 | Op::SetE
455 | Op::SetNE
456 | Op::SetG
457 | Op::SetGE
458 | Op::SetL
459 | Op::SetLE => {
460 let b = self.pop();
461 let a = self.pop();
462 let e = match &inst.op {
463 Op::Add => Expr::binary(BinOp::Add, a, b),
464 Op::Sub => Expr::binary(BinOp::Sub, a, b),
465 Op::Mul => Expr::binary(BinOp::Mul, a, b),
466 Op::Div => Expr::binary(BinOp::Div, a, b),
467 Op::Mod => Expr::binary(BinOp::Mod, a, b),
468 Op::BitTest => {
469 let and = Expr::binary(BinOp::BitAnd, a, b);
470 Expr::binary(BinOp::Ne, and, Expr::Int(0))
471 }
472 Op::And => {
473 let aa = Expr::binary(BinOp::Ne, a, Expr::Nil);
474 let bb = Expr::binary(BinOp::Ne, b, Expr::Nil);
475 Expr::binary(BinOp::And, aa, bb)
476 }
477 Op::Or => {
478 let aa = Expr::binary(BinOp::Ne, a, Expr::Nil);
479 let bb = Expr::binary(BinOp::Ne, b, Expr::Nil);
480 Expr::binary(BinOp::Or, aa, bb)
481 }
482 Op::SetE => Expr::binary(BinOp::Eq, a, b),
483 Op::SetNE => Expr::binary(BinOp::Ne, a, b),
484 Op::SetG => Expr::binary(BinOp::Gt, a, b),
485 Op::SetGE => Expr::binary(BinOp::Ge, a, b),
486 Op::SetL => Expr::binary(BinOp::Lt, a, b),
487 Op::SetLE => Expr::binary(BinOp::Le, a, b),
488 _ => Expr::Nil,
489 };
490 self.push(e);
491 }
492
493 Op::Call { target } => {
494 let argc = self.callee_args.get(target).copied().unwrap_or(0) as usize;
495 if self.stack.len() < argc {
496 self.emit_line(&format!(
497 "-- call f_{:08X} argc={} on short stack",
498 target, argc
499 ));
500 self.emit_line(&format!("__ret = f_{:08X}()", target));
501 self.stack.clear();
502 return;
503 }
504
505 let base = self.stack.len() - argc;
506 let mut args_s = String::new();
507 for i in 0..argc {
508 if i > 0 {
509 args_s.push_str(", ");
510 }
511 let a = self.stack[base + i].clone();
512 args_s.push_str(&a.render(self.used_s, 0));
513 }
514 self.emit_line(&format!("__ret = f_{:08X}({})", target, args_s));
515 self.stack.truncate(base);
516 }
517
518 Op::Syscall { name, args, id } => {
519 let argc = *args as usize;
520 if self.stack.len() < argc {
521 self.emit_line(&format!(
522 "-- syscall {} (id={}) argc={} on short stack",
523 name, id, argc
524 ));
525 self.emit_line(&format!("__ret = {}()", name));
526 self.stack.clear();
527 return;
528 }
529
530 let base = self.stack.len() - argc;
531 let mut args_s = String::new();
532 for i in 0..argc {
533 if i > 0 {
534 args_s.push_str(", ");
535 }
536 let a = self.stack[base + i].clone();
537 args_s.push_str(&a.render(self.used_s, 0));
538 }
539 self.emit_line(&format!("__ret = {}({})", name, args_s));
540 self.stack.truncate(base);
541 }
542
543 Op::Jmp { .. } | Op::Jz { .. } | Op::Ret | Op::RetV => {}
545
546 Op::Unknown(opcode) => {
547 self.emit_line(&format!("-- unknown opcode 0x{:02X}", opcode));
548 }
549 }
550 }
551
552 pub fn emit_terminator(&mut self, term: BlockTerm, succs: &[usize]) {
553 match term {
554 BlockTerm::Ret => {
555 self.emit_line("return");
556 }
557 BlockTerm::RetV => {
558 let v = self.pop();
559 let rhs = v.render(self.used_s, 0);
560 self.emit_line(&format!("return {}", rhs));
561 }
562 BlockTerm::Jmp | BlockTerm::Fallthrough => {
563 self.materialize_all_stack_slots();
564 if let Some(&t) = succs.get(0) {
565 self.emit_line(&format!("__pc = {}", t));
566 } else {
567 self.emit_line("return");
568 }
569 }
570 BlockTerm::Jz => {
571 let cond = self.pop();
572 self.materialize_all_stack_slots();
573 let t = succs.get(0).copied();
574 let f = succs.get(1).copied();
575
576 if let Some(is_zero) = cond.const_eq_zero() {
577 let dst = if is_zero { t } else { f };
579 match dst {
580 Some(id) => self.emit_line(&format!("__pc = {}", id)),
581 None => self.emit_line("return"),
582 }
583 return;
584 }
585
586 let c = cond.render(self.used_s, 0);
587 self.emit_line(&format!("if {} == 0 then", c));
588 match t {
589 Some(tid) => self.emit_line(&format!(" __pc = {}", tid)),
590 None => self.emit_line(" return"),
591 }
592 self.emit_line("else");
593 match f {
594 Some(fid) => self.emit_line(&format!(" __pc = {}", fid)),
595 None => self.emit_line(" return"),
596 }
597 self.emit_line("end");
598 }
599 }
600 }
601}