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