1use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::functions::*;
9use std::collections::HashSet;
10
11#[derive(Debug, Clone)]
13pub enum BeamVal {
14 Reg(BeamReg),
16 Int(i64),
18 Float(f64),
20 Atom(String),
22 Nil,
24 Literal(u32),
26}
27#[allow(dead_code)]
32#[derive(Debug, Clone)]
33pub struct BeamProcess {
34 pub pid_sym: String,
36 pub module: String,
38 pub init_fn: String,
40 pub init_args: Vec<BeamExpr>,
42 pub linked: bool,
44 pub monitored: bool,
46 pub dictionary: Vec<(String, BeamExpr)>,
48 pub trap_exit: bool,
50}
51impl BeamProcess {
52 #[allow(dead_code)]
54 pub fn new(
55 pid_sym: impl Into<String>,
56 module: impl Into<String>,
57 init_fn: impl Into<String>,
58 ) -> Self {
59 BeamProcess {
60 pid_sym: pid_sym.into(),
61 module: module.into(),
62 init_fn: init_fn.into(),
63 init_args: Vec::new(),
64 linked: false,
65 monitored: false,
66 dictionary: Vec::new(),
67 trap_exit: false,
68 }
69 }
70 #[allow(dead_code)]
72 pub fn with_arg(mut self, arg: BeamExpr) -> Self {
73 self.init_args.push(arg);
74 self
75 }
76 #[allow(dead_code)]
78 pub fn linked(mut self) -> Self {
79 self.linked = true;
80 self
81 }
82 #[allow(dead_code)]
84 pub fn trap_exit(mut self) -> Self {
85 self.trap_exit = true;
86 self
87 }
88 #[allow(dead_code)]
90 pub fn emit_spawn(&self) -> BeamExpr {
91 let args_list = self
92 .init_args
93 .iter()
94 .cloned()
95 .fold(BeamExpr::Nil, |tail, head| {
96 BeamExpr::Cons(Box::new(head), Box::new(tail))
97 });
98 let spawn_fn = if self.linked { "spawn_link" } else { "spawn" };
99 BeamExpr::Call {
100 module: Some("erlang".to_string()),
101 func: spawn_fn.to_string(),
102 args: vec![
103 BeamExpr::LitAtom(self.module.clone()),
104 BeamExpr::LitAtom(self.init_fn.clone()),
105 args_list,
106 ],
107 }
108 }
109}
110#[allow(dead_code)]
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113pub enum EtsAccess {
114 Private,
116 Protected,
118 Public,
120}
121#[allow(dead_code)]
123#[derive(Debug, Clone)]
124pub struct EtsTable {
125 pub name: String,
127 pub table_type: EtsType,
129 pub access: EtsAccess,
131 pub named_table: bool,
133 pub key_pos: u32,
135}
136impl EtsTable {
137 #[allow(dead_code)]
139 pub fn new_set(name: impl Into<String>) -> Self {
140 EtsTable {
141 name: name.into(),
142 table_type: EtsType::Set,
143 access: EtsAccess::Public,
144 named_table: true,
145 key_pos: 1,
146 }
147 }
148 #[allow(dead_code)]
150 pub fn emit_new(&self) -> BeamExpr {
151 let mut opts = vec![BeamExpr::LitAtom(self.table_type.to_string())];
152 if self.named_table {
153 opts.push(BeamExpr::LitAtom("named_table".to_string()));
154 }
155 opts.push(BeamExpr::LitAtom(self.access.to_string()));
156 if self.key_pos != 1 {
157 opts.push(BeamExpr::Tuple(vec![
158 BeamExpr::LitAtom("keypos".to_string()),
159 BeamExpr::LitInt(self.key_pos as i64),
160 ]));
161 }
162 let opts_list = opts.into_iter().fold(BeamExpr::Nil, |tail, head| {
163 BeamExpr::Cons(Box::new(head), Box::new(tail))
164 });
165 BeamExpr::Call {
166 module: Some("ets".to_string()),
167 func: "new".to_string(),
168 args: vec![BeamExpr::LitAtom(self.name.clone()), opts_list],
169 }
170 }
171 #[allow(dead_code)]
173 pub fn emit_insert(&self, tuple: BeamExpr) -> BeamExpr {
174 BeamExpr::Call {
175 module: Some("ets".to_string()),
176 func: "insert".to_string(),
177 args: vec![BeamExpr::LitAtom(self.name.clone()), tuple],
178 }
179 }
180 #[allow(dead_code)]
182 pub fn emit_lookup(&self, key: BeamExpr) -> BeamExpr {
183 BeamExpr::Call {
184 module: Some("ets".to_string()),
185 func: "lookup".to_string(),
186 args: vec![BeamExpr::LitAtom(self.name.clone()), key],
187 }
188 }
189 #[allow(dead_code)]
191 pub fn emit_delete(&self, key: BeamExpr) -> BeamExpr {
192 BeamExpr::Call {
193 module: Some("ets".to_string()),
194 func: "delete".to_string(),
195 args: vec![BeamExpr::LitAtom(self.name.clone()), key],
196 }
197 }
198}
199#[allow(dead_code)]
201#[derive(Debug, Clone, Default)]
202pub struct TailCallInfo {
203 pub self_tail_calls: u32,
205 pub external_tails: Vec<String>,
207 pub is_tail_recursive: bool,
209}
210impl TailCallInfo {
211 #[allow(dead_code)]
213 pub fn new() -> Self {
214 Self::default()
215 }
216 #[allow(dead_code)]
218 pub fn add_self_tail(&mut self) {
219 self.self_tail_calls += 1;
220 self.is_tail_recursive = true;
221 }
222 #[allow(dead_code)]
224 pub fn add_external_tail(&mut self, name: impl Into<String>) {
225 self.external_tails.push(name.into());
226 }
227 #[allow(dead_code)]
229 pub fn has_tail_calls(&self) -> bool {
230 self.self_tail_calls > 0 || !self.external_tails.is_empty()
231 }
232}
233#[derive(Debug, Clone)]
235pub struct BeamClause {
236 pub pattern: BeamPattern,
238 pub guard: Option<BeamExpr>,
240 pub body: BeamExpr,
242}
243pub struct BeamEmitCtx {
245 pub(super) label_counter: u32,
247 pub(super) var_counter: u32,
249 pub(super) indent: usize,
251 pub(super) output: String,
253}
254impl BeamEmitCtx {
255 pub fn new() -> Self {
256 BeamEmitCtx {
257 label_counter: 1,
258 var_counter: 0,
259 indent: 0,
260 output: String::new(),
261 }
262 }
263 pub(super) fn fresh_label(&mut self) -> u32 {
264 let l = self.label_counter;
265 self.label_counter += 1;
266 l
267 }
268 pub(super) fn fresh_var(&mut self) -> String {
269 let v = self.var_counter;
270 self.var_counter += 1;
271 format!("_V{}", v)
272 }
273 pub(super) fn indent_str(&self) -> String {
274 " ".repeat(self.indent)
275 }
276 pub(super) fn emit_line(&mut self, line: &str) {
277 let indent = self.indent_str();
278 self.output.push_str(&indent);
279 self.output.push_str(line);
280 self.output.push('\n');
281 }
282 pub(super) fn indented<F: FnOnce(&mut Self)>(&mut self, f: F) {
283 self.indent += 1;
284 f(self);
285 self.indent -= 1;
286 }
287}
288#[allow(dead_code)]
290#[derive(Debug, Clone, Default)]
291pub struct PatternNormalizer {
292 pub(super) wildcard_counter: u32,
294}
295impl PatternNormalizer {
296 #[allow(dead_code)]
298 pub fn new() -> Self {
299 Self::default()
300 }
301 #[allow(dead_code)]
303 pub fn normalize(&mut self, pat: BeamPattern) -> BeamPattern {
304 match pat {
305 BeamPattern::Var(_) => {
306 let n = self.wildcard_counter;
307 self.wildcard_counter += 1;
308 BeamPattern::Var(format!("_N{}", n))
309 }
310 BeamPattern::Alias(_, inner) => self.normalize(*inner),
311 BeamPattern::Cons(h, t) => {
312 let hn = self.normalize(*h);
313 let tn = self.normalize(*t);
314 BeamPattern::Cons(Box::new(hn), Box::new(tn))
315 }
316 BeamPattern::Tuple(pats) => {
317 BeamPattern::Tuple(pats.into_iter().map(|p| self.normalize(p)).collect())
318 }
319 other => other,
320 }
321 }
322 #[allow(dead_code)]
324 pub fn equivalent(&mut self, a: BeamPattern, b: BeamPattern) -> bool {
325 let na = self.normalize(a);
326 let nb = self.normalize(b);
327 patterns_structurally_equal(&na, &nb)
328 }
329}
330#[derive(Debug, Clone, PartialEq, Eq, Hash)]
335pub enum BeamType {
336 Integer,
338 Float,
340 Atom,
342 Pid,
344 Port,
346 Reference,
348 Binary,
350 List(Box<BeamType>),
352 Tuple(Vec<BeamType>),
354 Map(Box<BeamType>, Box<BeamType>),
356 Fun(Vec<BeamType>, Box<BeamType>),
358 Any,
360 None,
362 Union(Vec<BeamType>),
364 Named(String),
366}
367#[derive(Debug, Clone)]
369pub struct BeamFunction {
370 pub name: String,
372 pub arity: usize,
374 pub clauses: Vec<BeamClause>,
376 pub params: Vec<String>,
378 pub annotations: Vec<(String, String)>,
380 pub exported: bool,
382 pub instrs: Vec<BeamInstr>,
384 pub frame_size: u32,
386 pub return_type: Option<BeamType>,
388 pub param_types: Vec<BeamType>,
390}
391impl BeamFunction {
392 pub fn new(name: impl Into<String>, arity: usize) -> Self {
394 BeamFunction {
395 name: name.into(),
396 arity,
397 clauses: Vec::new(),
398 params: Vec::new(),
399 annotations: Vec::new(),
400 exported: false,
401 instrs: Vec::new(),
402 frame_size: 0,
403 return_type: None,
404 param_types: Vec::new(),
405 }
406 }
407 pub fn add_clause(&mut self, clause: BeamClause) {
409 self.clauses.push(clause);
410 }
411 pub fn annotate(&mut self, key: impl Into<String>, value: impl Into<String>) {
413 self.annotations.push((key.into(), value.into()));
414 }
415 pub fn export(&mut self) {
417 self.exported = true;
418 }
419 pub fn key(&self) -> String {
421 format!("{}/{}", self.name, self.arity)
422 }
423}
424#[allow(dead_code)]
426#[derive(Debug, Clone, Default)]
427pub struct BeamDeadEliminator {
428 pub(super) reachable: std::collections::HashSet<String>,
430}
431impl BeamDeadEliminator {
432 #[allow(dead_code)]
434 pub fn new() -> Self {
435 Self::default()
436 }
437 #[allow(dead_code)]
439 pub fn seed_exports(&mut self, module: &BeamModule) {
440 for (name, arity) in &module.exports {
441 self.reachable.insert(format!("{}/{}", name, arity));
442 }
443 }
444 #[allow(dead_code)]
446 pub fn eliminate(&self, module: BeamModule) -> BeamModule {
447 let mut result = BeamModule::new(module.name.clone());
448 result.attributes = module.attributes;
449 result.exports = module.exports;
450 result.on_load = module.on_load;
451 result.compile_info = module.compile_info;
452 for func in module.functions {
453 let key = format!("{}/{}", func.name, func.arity);
454 if self.reachable.contains(&key) || func.exported {
455 result.functions.push(func);
456 }
457 }
458 result
459 }
460 #[allow(dead_code)]
462 pub fn eliminated_count(before: &BeamModule, after: &BeamModule) -> usize {
463 before.functions.len().saturating_sub(after.functions.len())
464 }
465}
466#[allow(dead_code)]
468#[derive(Debug, Clone, Default)]
469pub struct AttributeBuilder {
470 pub(super) attrs: Vec<(String, String)>,
471}
472impl AttributeBuilder {
473 #[allow(dead_code)]
475 pub fn new() -> Self {
476 Self::default()
477 }
478 #[allow(dead_code)]
480 pub fn vsn(mut self, version: impl Into<String>) -> Self {
481 self.attrs.push(("vsn".into(), version.into()));
482 self
483 }
484 #[allow(dead_code)]
486 pub fn author(mut self, name: impl Into<String>) -> Self {
487 self.attrs.push(("author".into(), name.into()));
488 self
489 }
490 #[allow(dead_code)]
492 pub fn compile(mut self, option: impl Into<String>) -> Self {
493 self.attrs.push(("compile".into(), option.into()));
494 self
495 }
496 #[allow(dead_code)]
498 pub fn custom(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
499 self.attrs.push((key.into(), value.into()));
500 self
501 }
502 #[allow(dead_code)]
504 pub fn build(self) -> Vec<(String, String)> {
505 self.attrs
506 }
507 #[allow(dead_code)]
509 pub fn apply(self, module: &mut BeamModule) {
510 for (k, v) in self.build() {
511 module.add_attribute(k, v);
512 }
513 }
514}
515#[derive(Debug, Clone)]
517pub enum BeamPattern {
518 Wildcard,
520 Var(String),
522 LitInt(i64),
524 LitAtom(String),
526 LitString(String),
528 Nil,
530 Cons(Box<BeamPattern>, Box<BeamPattern>),
532 Tuple(Vec<BeamPattern>),
534 MapPat(Vec<(BeamExpr, BeamPattern)>),
536 Alias(String, Box<BeamPattern>),
538}
539#[derive(Debug, Clone, PartialEq, Eq)]
541pub enum BeamEndian {
542 Big,
543 Little,
544 Native,
545}
546#[allow(dead_code)]
550#[derive(Debug, Clone, Default)]
551pub struct BeamTypeCtx {
552 pub(super) env: HashMap<String, BeamType>,
554 pub(super) fun_types: HashMap<String, (Vec<BeamType>, BeamType)>,
556}
557impl BeamTypeCtx {
558 #[allow(dead_code)]
560 pub fn new() -> Self {
561 Self::default()
562 }
563 #[allow(dead_code)]
565 pub fn bind(&mut self, var: impl Into<String>, ty: BeamType) {
566 self.env.insert(var.into(), ty);
567 }
568 #[allow(dead_code)]
570 pub fn lookup(&self, var: &str) -> Option<&BeamType> {
571 self.env.get(var)
572 }
573 #[allow(dead_code)]
575 pub fn register_fun(&mut self, name: impl Into<String>, params: Vec<BeamType>, ret: BeamType) {
576 self.fun_types.insert(name.into(), (params, ret));
577 }
578 #[allow(dead_code)]
580 pub fn infer(&self, expr: &BeamExpr) -> BeamType {
581 match expr {
582 BeamExpr::LitInt(_) => BeamType::Integer,
583 BeamExpr::LitFloat(_) => BeamType::Float,
584 BeamExpr::LitAtom(_) => BeamType::Atom,
585 BeamExpr::LitString(_) => BeamType::Named("binary".to_string()),
586 BeamExpr::Nil => BeamType::List(Box::new(BeamType::Any)),
587 BeamExpr::Var(name) => self
588 .env
589 .get(name.as_str())
590 .cloned()
591 .unwrap_or(BeamType::Any),
592 BeamExpr::Tuple(elems) => {
593 BeamType::Tuple(elems.iter().map(|e| self.infer(e)).collect())
594 }
595 BeamExpr::Cons(head, _) => BeamType::List(Box::new(self.infer(head))),
596 BeamExpr::Map(_) => BeamType::Map(Box::new(BeamType::Any), Box::new(BeamType::Any)),
597 BeamExpr::Fun { params, .. } => BeamType::Fun(
598 params.iter().map(|_| BeamType::Any).collect(),
599 Box::new(BeamType::Any),
600 ),
601 _ => BeamType::Any,
602 }
603 }
604 #[allow(dead_code)]
606 pub fn merge(&self, other: &BeamTypeCtx) -> BeamTypeCtx {
607 let mut merged = self.clone();
608 for (k, v) in &other.env {
609 if let Some(existing) = merged.env.get(k) {
610 if existing != v {
611 merged.env.insert(k.clone(), BeamType::Any);
612 }
613 } else {
614 merged.env.insert(k.clone(), v.clone());
615 }
616 }
617 merged
618 }
619}
620#[derive(Debug, Clone)]
625pub enum BeamInstr {
626 Label(u32),
628 FuncInfo {
630 module: String,
631 function: String,
632 arity: u32,
633 },
634 Call { arity: u32, label: u32 },
636 CallLast {
638 arity: u32,
639 label: u32,
640 deallocate: u32,
641 },
642 CallExt {
644 arity: u32,
645 destination: BeamExtFunc,
646 },
647 CallExtLast {
649 arity: u32,
650 destination: BeamExtFunc,
651 deallocate: u32,
652 },
653 CallFun { arity: u32 },
655 Move { src: BeamReg, dst: BeamReg },
657 PutTuple { arity: u32, dst: BeamReg },
659 Put(BeamVal),
661 GetTupleElement {
663 src: BeamReg,
664 index: u32,
665 dst: BeamReg,
666 },
667 SetTupleElement {
669 value: BeamVal,
670 tuple: BeamReg,
671 index: u32,
672 },
673 IsEq {
675 fail: u32,
676 lhs: BeamVal,
677 rhs: BeamVal,
678 },
679 IsEqExact {
681 fail: u32,
682 lhs: BeamVal,
683 rhs: BeamVal,
684 },
685 IsNe {
687 fail: u32,
688 lhs: BeamVal,
689 rhs: BeamVal,
690 },
691 IsLt {
693 fail: u32,
694 lhs: BeamVal,
695 rhs: BeamVal,
696 },
697 IsGe {
699 fail: u32,
700 lhs: BeamVal,
701 rhs: BeamVal,
702 },
703 IsInteger { fail: u32, arg: BeamVal },
705 IsFloat { fail: u32, arg: BeamVal },
707 IsAtom { fail: u32, arg: BeamVal },
709 IsNil { fail: u32, arg: BeamVal },
711 IsList { fail: u32, arg: BeamVal },
713 IsTuple { fail: u32, arg: BeamVal },
715 IsBinary { fail: u32, arg: BeamVal },
717 IsFunction { fail: u32, arg: BeamVal },
719 Jump(u32),
721 Return,
723 Send,
725 RemoveMessage,
727 LoopRec { fail: u32, dst: BeamReg },
729 Wait(u32),
731 WaitTimeout { fail: u32, timeout: BeamVal },
733 GcBif {
735 name: String,
736 fail: u32,
737 live: u32,
738 args: Vec<BeamVal>,
739 dst: BeamReg,
740 },
741 Bif0 { name: String, dst: BeamReg },
743 Allocate { stack_need: u32, live: u32 },
745 Deallocate(u32),
747 Init(BeamReg),
749 MakeFun2(u32),
751 GetList {
753 src: BeamReg,
754 head: BeamReg,
755 tail: BeamReg,
756 },
757 PutList {
759 head: BeamVal,
760 tail: BeamVal,
761 dst: BeamReg,
762 },
763 Raise { class: BeamVal, reason: BeamVal },
765 TryBegin { label: u32, reg: BeamReg },
767 TryEnd(BeamReg),
769 TryCase(BeamReg),
771 Comment(String),
773}
774#[derive(Debug, Clone)]
776pub struct BeamExtFunc {
777 pub module: String,
778 pub function: String,
779 pub arity: u32,
780}
781#[derive(Debug, Clone)]
783pub struct BeamBitSegment {
784 pub value: BeamExpr,
786 pub size: Option<BeamExpr>,
788 pub type_spec: String,
790 pub signed: bool,
792 pub endian: BeamEndian,
794 pub unit: Option<u8>,
796}
797#[allow(dead_code)]
799#[derive(Debug, Clone, Copy, PartialEq, Eq)]
800pub enum EtsType {
801 Set,
803 OrderedSet,
805 Bag,
807 DuplicateBag,
809}
810#[derive(Debug, Clone)]
815pub enum BeamExpr {
816 LitInt(i64),
818 LitFloat(f64),
820 LitAtom(String),
822 LitString(String),
824 Nil,
826 Var(String),
828 Tuple(Vec<BeamExpr>),
830 Cons(Box<BeamExpr>, Box<BeamExpr>),
832 Map(Vec<(BeamExpr, BeamExpr)>),
834 MapUpdate(Box<BeamExpr>, Vec<(BeamExpr, BeamExpr)>),
836 Call {
838 module: Option<String>,
839 func: String,
840 args: Vec<BeamExpr>,
841 },
842 CallExt {
844 module: Box<BeamExpr>,
845 func: Box<BeamExpr>,
846 args: Vec<BeamExpr>,
847 },
848 Apply {
850 fun: Box<BeamExpr>,
851 args: Vec<BeamExpr>,
852 },
853 Case {
855 subject: Box<BeamExpr>,
856 clauses: Vec<BeamClause>,
857 },
858 Let {
860 var: String,
861 value: Box<BeamExpr>,
862 body: Box<BeamExpr>,
863 },
864 Letrec {
866 bindings: Vec<(String, Vec<String>, Box<BeamExpr>)>,
867 body: Box<BeamExpr>,
868 },
869 Fun {
871 params: Vec<String>,
872 body: Box<BeamExpr>,
873 arity: usize,
874 },
875 Primop { name: String, args: Vec<BeamExpr> },
877 Receive {
879 clauses: Vec<BeamClause>,
880 timeout: Option<Box<BeamExpr>>,
881 timeout_action: Option<Box<BeamExpr>>,
882 },
883 Try {
885 body: Box<BeamExpr>,
886 vars: Vec<String>,
887 handler: Box<BeamExpr>,
888 evars: Vec<String>,
889 catch_body: Box<BeamExpr>,
890 },
891 Seq(Box<BeamExpr>, Box<BeamExpr>),
893 Binary(Vec<BeamBitSegment>),
895}
896#[allow(dead_code)]
900#[derive(Debug, Clone)]
901pub struct GenServerSpec {
902 pub module_name: String,
904 pub state_doc: String,
906 pub handle_calls: Vec<(BeamPattern, BeamExpr)>,
908 pub handle_casts: Vec<(BeamPattern, BeamExpr)>,
910 pub handle_infos: Vec<(BeamPattern, BeamExpr)>,
912 pub init_state: BeamExpr,
914 pub terminate_body: Option<BeamExpr>,
916}
917impl GenServerSpec {
918 #[allow(dead_code)]
920 pub fn new(module_name: impl Into<String>, init_state: BeamExpr) -> Self {
921 GenServerSpec {
922 module_name: module_name.into(),
923 state_doc: "State".to_string(),
924 handle_calls: Vec::new(),
925 handle_casts: Vec::new(),
926 handle_infos: Vec::new(),
927 init_state,
928 terminate_body: None,
929 }
930 }
931 #[allow(dead_code)]
933 pub fn generate_module(&self) -> BeamModule {
934 let mut module = BeamModule::new(self.module_name.clone());
935 module.add_attribute("behaviour", "gen_server");
936 let mut start_link = BeamFunction::new("start_link", 0);
937 start_link.export();
938 module.add_function(start_link);
939 let mut init_fn = BeamFunction::new("init", 1);
940 init_fn.export();
941 module.add_function(init_fn);
942 let mut handle_call = BeamFunction::new("handle_call", 3);
943 handle_call.export();
944 module.add_function(handle_call);
945 let mut handle_cast = BeamFunction::new("handle_cast", 2);
946 handle_cast.export();
947 module.add_function(handle_cast);
948 let mut handle_info = BeamFunction::new("handle_info", 2);
949 handle_info.export();
950 module.add_function(handle_info);
951 let mut terminate = BeamFunction::new("terminate", 2);
952 terminate.export();
953 module.add_function(terminate);
954 module
955 }
956 #[allow(dead_code)]
958 pub fn emit_init(&self) -> String {
959 format!("init([]) ->\n {{ok, {}}}.\n", "State")
960 }
961}
962#[allow(dead_code)]
967#[derive(Debug, Clone, Default)]
968pub struct BeamLinker {
969 pub(super) modules: Vec<BeamModule>,
971 pub(super) rename_map: HashMap<(String, String), String>,
973 pub(super) target_name: String,
975}
976impl BeamLinker {
977 #[allow(dead_code)]
979 pub fn new(target: impl Into<String>) -> Self {
980 BeamLinker {
981 modules: Vec::new(),
982 rename_map: HashMap::new(),
983 target_name: target.into(),
984 }
985 }
986 #[allow(dead_code)]
988 pub fn add_module(&mut self, module: BeamModule) {
989 self.modules.push(module);
990 }
991 #[allow(dead_code)]
993 pub fn rename(
994 &mut self,
995 src_module: impl Into<String>,
996 src_name: impl Into<String>,
997 new_name: impl Into<String>,
998 ) {
999 self.rename_map
1000 .insert((src_module.into(), src_name.into()), new_name.into());
1001 }
1002 #[allow(dead_code)]
1004 pub fn link(self) -> BeamModule {
1005 let mut result = BeamModule::new(self.target_name);
1006 for module in self.modules {
1007 for mut func in module.functions {
1008 let key = (module.name.clone(), func.name.clone());
1009 if let Some(new_name) = self.rename_map.get(&key) {
1010 func.name = new_name.clone();
1011 }
1012 result.add_function(func);
1013 }
1014 for attr in module.attributes {
1015 result.attributes.push(attr);
1016 }
1017 }
1018 result
1019 }
1020}
1021#[allow(dead_code)]
1026#[derive(Debug, Clone, Default)]
1027pub struct BeamConstPool {
1028 pub(super) literals: Vec<BeamExpr>,
1030 pub(super) index_map: HashMap<String, u32>,
1032}
1033impl BeamConstPool {
1034 #[allow(dead_code)]
1036 pub fn new() -> Self {
1037 Self::default()
1038 }
1039 #[allow(dead_code)]
1041 pub fn intern(&mut self, expr: BeamExpr, key: impl Into<String>) -> u32 {
1042 let k = key.into();
1043 if let Some(&idx) = self.index_map.get(&k) {
1044 return idx;
1045 }
1046 let idx = self.literals.len() as u32;
1047 self.literals.push(expr);
1048 self.index_map.insert(k, idx);
1049 idx
1050 }
1051 #[allow(dead_code)]
1053 pub fn get(&self, idx: u32) -> Option<&BeamExpr> {
1054 self.literals.get(idx as usize)
1055 }
1056 #[allow(dead_code)]
1058 pub fn len(&self) -> usize {
1059 self.literals.len()
1060 }
1061 #[allow(dead_code)]
1063 pub fn is_empty(&self) -> bool {
1064 self.literals.is_empty()
1065 }
1066}
1067#[allow(dead_code)]
1072#[derive(Debug, Clone, Default)]
1073pub struct XRegAllocator {
1074 pub(super) next_x: u32,
1076 pub(super) assignment: HashMap<String, u32>,
1078 pub(super) max_x: u32,
1080}
1081impl XRegAllocator {
1082 #[allow(dead_code)]
1084 pub fn new() -> Self {
1085 Self::default()
1086 }
1087 #[allow(dead_code)]
1089 pub fn alloc(&mut self, var: impl Into<String>) -> u32 {
1090 let x = self.next_x;
1091 let name = var.into();
1092 self.assignment.insert(name, x);
1093 if x > self.max_x {
1094 self.max_x = x;
1095 }
1096 self.next_x += 1;
1097 x
1098 }
1099 #[allow(dead_code)]
1101 pub fn get(&self, var: &str) -> Option<u32> {
1102 self.assignment.get(var).copied()
1103 }
1104 #[allow(dead_code)]
1106 pub fn reset(&mut self) {
1107 self.next_x = 0;
1108 self.assignment.clear();
1109 self.max_x = 0;
1110 }
1111 #[allow(dead_code)]
1113 pub fn registers_used(&self) -> u32 {
1114 if self.assignment.is_empty() {
1115 0
1116 } else {
1117 self.max_x + 1
1118 }
1119 }
1120}
1121pub struct BeamBackend {
1126 pub module: BeamModule,
1128 pub(super) ctx: BeamEmitCtx,
1130 pub(super) var_map: HashMap<u64, String>,
1132 pub(super) next_label: u32,
1134}
1135impl BeamBackend {
1136 pub fn new(module_name: impl Into<String>) -> Self {
1138 BeamBackend {
1139 module: BeamModule::new(module_name),
1140 ctx: BeamEmitCtx::new(),
1141 var_map: HashMap::new(),
1142 next_label: 1,
1143 }
1144 }
1145 pub(super) fn fresh_label(&mut self) -> u32 {
1147 let l = self.next_label;
1148 self.next_label += 1;
1149 l
1150 }
1151 pub(super) fn beam_var(&mut self, id: LcnfVarId) -> String {
1153 self.var_map
1154 .entry(id.0)
1155 .or_insert_with(|| format!("_X{}", id.0))
1156 .clone()
1157 }
1158 pub fn emit_literal(&self, lit: &LcnfLit) -> BeamExpr {
1160 match lit {
1161 LcnfLit::Nat(n) => BeamExpr::LitInt(*n as i64),
1162 LcnfLit::Str(s) => BeamExpr::LitString(s.clone()),
1163 }
1164 }
1165 pub fn emit_arg(&mut self, arg: &LcnfArg) -> BeamExpr {
1167 match arg {
1168 LcnfArg::Var(id) => BeamExpr::Var(self.beam_var(*id)),
1169 LcnfArg::Lit(lit) => self.emit_literal(lit),
1170 LcnfArg::Erased => BeamExpr::LitAtom("erased".to_string()),
1171 LcnfArg::Type(_) => BeamExpr::LitAtom("type".to_string()),
1172 }
1173 }
1174 #[allow(clippy::too_many_arguments)]
1176 pub fn emit_let_value(&mut self, val: &LcnfLetValue) -> BeamExpr {
1177 match val {
1178 LcnfLetValue::App(func, args) => {
1179 let func_expr = self.emit_arg(func);
1180 let arg_exprs: Vec<BeamExpr> = args.iter().map(|a| self.emit_arg(a)).collect();
1181 BeamExpr::Apply {
1182 fun: Box::new(func_expr),
1183 args: arg_exprs,
1184 }
1185 }
1186 LcnfLetValue::Proj(struct_name, idx, var) => {
1187 let var_expr = BeamExpr::Var(self.beam_var(*var));
1188 let beam_idx = (*idx as i64) + 2;
1189 BeamExpr::Primop {
1190 name: format!("element({}, {})", beam_idx, struct_name),
1191 args: vec![var_expr],
1192 }
1193 }
1194 LcnfLetValue::Ctor(name, _tag, args) => {
1195 let mut elems = vec![BeamExpr::LitAtom(sanitize_atom(name))];
1196 for a in args {
1197 elems.push(self.emit_arg(a));
1198 }
1199 BeamExpr::Tuple(elems)
1200 }
1201 LcnfLetValue::Lit(lit) => self.emit_literal(lit),
1202 LcnfLetValue::Erased => BeamExpr::LitAtom("erased".to_string()),
1203 LcnfLetValue::FVar(id) => BeamExpr::Var(self.beam_var(*id)),
1204 LcnfLetValue::Reset(var) => BeamExpr::Primop {
1205 name: "reset".to_string(),
1206 args: vec![BeamExpr::Var(self.beam_var(*var))],
1207 },
1208 LcnfLetValue::Reuse(slot, name, _tag, args) => {
1209 let slot_expr = BeamExpr::Var(self.beam_var(*slot));
1210 let mut elems = vec![BeamExpr::LitAtom(sanitize_atom(name)), slot_expr];
1211 for a in args {
1212 elems.push(self.emit_arg(a));
1213 }
1214 BeamExpr::Tuple(elems)
1215 }
1216 }
1217 }
1218 #[allow(clippy::too_many_arguments)]
1220 pub fn emit_expr(&mut self, expr: &LcnfExpr) -> BeamExpr {
1221 match expr {
1222 LcnfExpr::Let {
1223 id, value, body, ..
1224 } => {
1225 let vname = self.beam_var(*id);
1226 let val_expr = self.emit_let_value(value);
1227 let body_expr = self.emit_expr(body);
1228 BeamExpr::Let {
1229 var: vname,
1230 value: Box::new(val_expr),
1231 body: Box::new(body_expr),
1232 }
1233 }
1234 LcnfExpr::Case {
1235 scrutinee,
1236 alts,
1237 default,
1238 ..
1239 } => {
1240 let subj_expr = BeamExpr::Var(self.beam_var(*scrutinee));
1241 let mut clauses: Vec<BeamClause> =
1242 alts.iter().map(|alt| self.emit_case_alt(alt)).collect();
1243 if let Some(def_body) = default {
1244 let def_expr = self.emit_expr(def_body);
1245 clauses.push(BeamClause {
1246 pattern: BeamPattern::Wildcard,
1247 guard: None,
1248 body: def_expr,
1249 });
1250 }
1251 BeamExpr::Case {
1252 subject: Box::new(subj_expr),
1253 clauses,
1254 }
1255 }
1256 LcnfExpr::Return(arg) => self.emit_arg(arg),
1257 LcnfExpr::Unreachable => BeamExpr::Primop {
1258 name: "error".to_string(),
1259 args: vec![BeamExpr::Tuple(vec![
1260 BeamExpr::LitAtom("error".to_string()),
1261 BeamExpr::LitAtom("unreachable".to_string()),
1262 ])],
1263 },
1264 LcnfExpr::TailCall(func, args) => {
1265 let func_expr = self.emit_arg(func);
1266 let arg_exprs: Vec<BeamExpr> = args.iter().map(|a| self.emit_arg(a)).collect();
1267 BeamExpr::Apply {
1268 fun: Box::new(func_expr),
1269 args: arg_exprs,
1270 }
1271 }
1272 }
1273 }
1274 pub(super) fn emit_case_alt(&mut self, alt: &LcnfAlt) -> BeamClause {
1276 let mut pats = vec![BeamPattern::LitAtom(sanitize_atom(&alt.ctor_name))];
1277 for param in &alt.params {
1278 pats.push(BeamPattern::Var(self.beam_var(param.id)));
1279 }
1280 let body = self.emit_expr(&alt.body);
1281 BeamClause {
1282 pattern: BeamPattern::Tuple(pats),
1283 guard: None,
1284 body,
1285 }
1286 }
1287 pub fn emit_fun_decl(&mut self, decl: &LcnfFunDecl) -> BeamFunction {
1289 let mut func = BeamFunction::new(sanitize_atom(&decl.name), decl.params.len());
1290 func.params = decl.params.iter().map(|p| self.beam_var(p.id)).collect();
1291 let patterns = func
1292 .params
1293 .iter()
1294 .map(|p| BeamPattern::Var(p.clone()))
1295 .collect();
1296 let body = self.emit_expr(&decl.body);
1297 func.add_clause(BeamClause {
1298 pattern: BeamPattern::Tuple(patterns),
1299 guard: None,
1300 body,
1301 });
1302 self.lower_function(&mut func);
1303 func
1304 }
1305 pub(super) fn lower_function(&mut self, func: &mut BeamFunction) {
1307 let entry = self.fresh_label();
1308 func.instrs.push(BeamInstr::Label(entry));
1309 func.instrs.push(BeamInstr::FuncInfo {
1310 module: self.module.name.clone(),
1311 function: func.name.clone(),
1312 arity: func.arity as u32,
1313 });
1314 let body_label = self.fresh_label();
1315 func.instrs.push(BeamInstr::Label(body_label));
1316 for (i, _param) in func.params.iter().enumerate() {
1317 func.instrs.push(BeamInstr::Move {
1318 src: BeamReg::X(i as u32),
1319 dst: BeamReg::Y(i as u32),
1320 });
1321 }
1322 func.instrs.push(BeamInstr::Return);
1323 }
1324 pub fn emit_core_erlang(&mut self) -> String {
1326 let mut out = String::new();
1327 out.push_str(&format!("module '{}'\n", self.module.name));
1328 out.push_str(" [");
1329 let exports = self.module.export_list();
1330 for (i, exp) in exports.iter().enumerate() {
1331 if i > 0 {
1332 out.push_str(", ");
1333 }
1334 out.push_str(&format!("'{}'", exp));
1335 }
1336 out.push_str("]\n");
1337 out.push_str(" attributes []\n");
1338 for func in &self.module.functions.clone() {
1339 out.push_str(&self.emit_function_core_erlang(func));
1340 }
1341 out.push_str("end\n");
1342 out
1343 }
1344 pub(super) fn emit_function_core_erlang(&self, func: &BeamFunction) -> String {
1346 let mut out = String::new();
1347 let params_str = func
1348 .params
1349 .iter()
1350 .map(|p| format!("_{}", p))
1351 .collect::<Vec<_>>()
1352 .join(", ");
1353 out.push_str(&format!(
1354 "\n'{}'/{} =\n fun ({}) ->\n",
1355 func.name, func.arity, params_str
1356 ));
1357 out.push_str(" 'ok'\n");
1358 out
1359 }
1360 pub fn emit_asm(&self) -> String {
1362 let mut out = String::new();
1363 out.push_str(&format!("{{module, '{}'}}.\n\n", self.module.name));
1364 out.push_str(&format!(
1365 "{{exports, [{}]}}.\n\n",
1366 self.module
1367 .exports
1368 .iter()
1369 .map(|(n, a)| format!("{{{}, {}}}", n, a))
1370 .collect::<Vec<_>>()
1371 .join(", ")
1372 ));
1373 for func in &self.module.functions {
1374 out.push_str(&format!("%% Function: {}/{}\n", func.name, func.arity));
1375 for instr in &func.instrs {
1376 out.push_str(&format!(" {}\n", emit_instr(instr)));
1377 }
1378 out.push('\n');
1379 }
1380 out
1381 }
1382}
1383#[allow(dead_code)]
1385pub struct BeamPrinter {
1386 pub(super) indent: usize,
1387 pub(super) buf: String,
1388}
1389impl BeamPrinter {
1390 #[allow(dead_code)]
1392 pub fn new() -> Self {
1393 BeamPrinter {
1394 indent: 0,
1395 buf: String::new(),
1396 }
1397 }
1398 pub(super) fn pad(&self) -> String {
1399 " ".repeat(self.indent)
1400 }
1401 pub(super) fn push(&mut self, s: &str) {
1402 self.buf.push_str(s);
1403 }
1404 pub(super) fn newline(&mut self) {
1405 self.buf.push('\n');
1406 self.buf.push_str(&self.pad());
1407 }
1408 #[allow(dead_code)]
1410 pub fn print_expr(&mut self, expr: &BeamExpr) {
1411 match expr {
1412 BeamExpr::LitInt(n) => self.push(&n.to_string()),
1413 BeamExpr::LitFloat(v) => self.push(&format!("{:.6}", v)),
1414 BeamExpr::LitAtom(a) => self.push(&format!("'{}'", a)),
1415 BeamExpr::LitString(s) => self.push(&format!("\"{}\"", s)),
1416 BeamExpr::Nil => self.push("[]"),
1417 BeamExpr::Var(name) => self.push(name),
1418 BeamExpr::Tuple(elems) => {
1419 self.push("{");
1420 for (i, e) in elems.iter().enumerate() {
1421 if i > 0 {
1422 self.push(", ");
1423 }
1424 self.print_expr(e);
1425 }
1426 self.push("}");
1427 }
1428 BeamExpr::Cons(head, tail) => {
1429 self.push("[");
1430 self.print_expr(head);
1431 self.push(" | ");
1432 self.print_expr(tail);
1433 self.push("]");
1434 }
1435 BeamExpr::Let { var, value, body } => {
1436 self.push(&format!("let {} =", var));
1437 self.indent += 1;
1438 self.newline();
1439 self.print_expr(value);
1440 self.indent -= 1;
1441 self.newline();
1442 self.push("in ");
1443 self.print_expr(body);
1444 }
1445 BeamExpr::Apply { fun, args } => {
1446 self.push("apply ");
1447 self.print_expr(fun);
1448 self.push("(");
1449 for (i, a) in args.iter().enumerate() {
1450 if i > 0 {
1451 self.push(", ");
1452 }
1453 self.print_expr(a);
1454 }
1455 self.push(")");
1456 }
1457 BeamExpr::Call { module, func, args } => {
1458 if let Some(m) = module {
1459 self.push(&format!("call '{}':'{}' (", m, func));
1460 } else {
1461 self.push(&format!("call '{}' (", func));
1462 }
1463 for (i, a) in args.iter().enumerate() {
1464 if i > 0 {
1465 self.push(", ");
1466 }
1467 self.print_expr(a);
1468 }
1469 self.push(")");
1470 }
1471 BeamExpr::Primop { name, args } => {
1472 self.push(&format!("primop '{}' (", name));
1473 for (i, a) in args.iter().enumerate() {
1474 if i > 0 {
1475 self.push(", ");
1476 }
1477 self.print_expr(a);
1478 }
1479 self.push(")");
1480 }
1481 _ => self.push("..."),
1482 }
1483 }
1484 #[allow(dead_code)]
1486 pub fn finish(self) -> String {
1487 self.buf
1488 }
1489}
1490#[derive(Debug, Clone)]
1492pub struct BeamModule {
1493 pub name: String,
1495 pub attributes: Vec<(String, String)>,
1497 pub functions: Vec<BeamFunction>,
1499 pub exports: Vec<(String, usize)>,
1501 pub on_load: Option<String>,
1503 pub compile_info: HashMap<String, String>,
1505}
1506impl BeamModule {
1507 pub fn new(name: impl Into<String>) -> Self {
1509 BeamModule {
1510 name: name.into(),
1511 attributes: Vec::new(),
1512 functions: Vec::new(),
1513 exports: Vec::new(),
1514 on_load: None,
1515 compile_info: HashMap::new(),
1516 }
1517 }
1518 pub fn add_function(&mut self, func: BeamFunction) {
1520 if func.exported {
1521 self.exports.push((func.name.clone(), func.arity));
1522 }
1523 self.functions.push(func);
1524 }
1525 pub fn add_attribute(&mut self, key: impl Into<String>, value: impl Into<String>) {
1527 self.attributes.push((key.into(), value.into()));
1528 }
1529 pub fn find_function(&self, name: &str, arity: usize) -> Option<&BeamFunction> {
1531 self.functions
1532 .iter()
1533 .find(|f| f.name == name && f.arity == arity)
1534 }
1535 pub fn export_list(&self) -> Vec<String> {
1537 self.exports
1538 .iter()
1539 .map(|(n, a)| format!("{}/{}", n, a))
1540 .collect()
1541 }
1542}
1543#[derive(Debug, Clone, PartialEq)]
1545pub enum BeamReg {
1546 X(u32),
1548 Y(u32),
1550 FR(u32),
1552}