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::Int(i) => BeamExpr::LitInt(*i),
1163 LcnfLit::Str(s) => BeamExpr::LitString(s.clone()),
1164 }
1165 }
1166 pub fn emit_arg(&mut self, arg: &LcnfArg) -> BeamExpr {
1168 match arg {
1169 LcnfArg::Var(id) => BeamExpr::Var(self.beam_var(*id)),
1170 LcnfArg::Lit(lit) => self.emit_literal(lit),
1171 LcnfArg::Erased => BeamExpr::LitAtom("erased".to_string()),
1172 LcnfArg::Type(_) => BeamExpr::LitAtom("type".to_string()),
1173 }
1174 }
1175 #[allow(clippy::too_many_arguments)]
1177 pub fn emit_let_value(&mut self, val: &LcnfLetValue) -> BeamExpr {
1178 match val {
1179 LcnfLetValue::App(func, args) => {
1180 let func_expr = self.emit_arg(func);
1181 let arg_exprs: Vec<BeamExpr> = args.iter().map(|a| self.emit_arg(a)).collect();
1182 BeamExpr::Apply {
1183 fun: Box::new(func_expr),
1184 args: arg_exprs,
1185 }
1186 }
1187 LcnfLetValue::Proj(struct_name, idx, var) => {
1188 let var_expr = BeamExpr::Var(self.beam_var(*var));
1189 let beam_idx = (*idx as i64) + 2;
1190 BeamExpr::Primop {
1191 name: format!("element({}, {})", beam_idx, struct_name),
1192 args: vec![var_expr],
1193 }
1194 }
1195 LcnfLetValue::Ctor(name, _tag, args) => {
1196 let mut elems = vec![BeamExpr::LitAtom(sanitize_atom(name))];
1197 for a in args {
1198 elems.push(self.emit_arg(a));
1199 }
1200 BeamExpr::Tuple(elems)
1201 }
1202 LcnfLetValue::Lit(lit) => self.emit_literal(lit),
1203 LcnfLetValue::Erased => BeamExpr::LitAtom("erased".to_string()),
1204 LcnfLetValue::FVar(id) => BeamExpr::Var(self.beam_var(*id)),
1205 LcnfLetValue::Reset(var) => BeamExpr::Primop {
1206 name: "reset".to_string(),
1207 args: vec![BeamExpr::Var(self.beam_var(*var))],
1208 },
1209 LcnfLetValue::Reuse(slot, name, _tag, args) => {
1210 let slot_expr = BeamExpr::Var(self.beam_var(*slot));
1211 let mut elems = vec![BeamExpr::LitAtom(sanitize_atom(name)), slot_expr];
1212 for a in args {
1213 elems.push(self.emit_arg(a));
1214 }
1215 BeamExpr::Tuple(elems)
1216 }
1217 }
1218 }
1219 #[allow(clippy::too_many_arguments)]
1221 pub fn emit_expr(&mut self, expr: &LcnfExpr) -> BeamExpr {
1222 match expr {
1223 LcnfExpr::Let {
1224 id, value, body, ..
1225 } => {
1226 let vname = self.beam_var(*id);
1227 let val_expr = self.emit_let_value(value);
1228 let body_expr = self.emit_expr(body);
1229 BeamExpr::Let {
1230 var: vname,
1231 value: Box::new(val_expr),
1232 body: Box::new(body_expr),
1233 }
1234 }
1235 LcnfExpr::Case {
1236 scrutinee,
1237 alts,
1238 default,
1239 ..
1240 } => {
1241 let subj_expr = BeamExpr::Var(self.beam_var(*scrutinee));
1242 let mut clauses: Vec<BeamClause> =
1243 alts.iter().map(|alt| self.emit_case_alt(alt)).collect();
1244 if let Some(def_body) = default {
1245 let def_expr = self.emit_expr(def_body);
1246 clauses.push(BeamClause {
1247 pattern: BeamPattern::Wildcard,
1248 guard: None,
1249 body: def_expr,
1250 });
1251 }
1252 BeamExpr::Case {
1253 subject: Box::new(subj_expr),
1254 clauses,
1255 }
1256 }
1257 LcnfExpr::Return(arg) => self.emit_arg(arg),
1258 LcnfExpr::Unreachable => BeamExpr::Primop {
1259 name: "error".to_string(),
1260 args: vec![BeamExpr::Tuple(vec![
1261 BeamExpr::LitAtom("error".to_string()),
1262 BeamExpr::LitAtom("unreachable".to_string()),
1263 ])],
1264 },
1265 LcnfExpr::TailCall(func, args) => {
1266 let func_expr = self.emit_arg(func);
1267 let arg_exprs: Vec<BeamExpr> = args.iter().map(|a| self.emit_arg(a)).collect();
1268 BeamExpr::Apply {
1269 fun: Box::new(func_expr),
1270 args: arg_exprs,
1271 }
1272 }
1273 }
1274 }
1275 pub(super) fn emit_case_alt(&mut self, alt: &LcnfAlt) -> BeamClause {
1277 let mut pats = vec![BeamPattern::LitAtom(sanitize_atom(&alt.ctor_name))];
1278 for param in &alt.params {
1279 pats.push(BeamPattern::Var(self.beam_var(param.id)));
1280 }
1281 let body = self.emit_expr(&alt.body);
1282 BeamClause {
1283 pattern: BeamPattern::Tuple(pats),
1284 guard: None,
1285 body,
1286 }
1287 }
1288 pub fn emit_fun_decl(&mut self, decl: &LcnfFunDecl) -> BeamFunction {
1290 let mut func = BeamFunction::new(sanitize_atom(&decl.name), decl.params.len());
1291 func.params = decl.params.iter().map(|p| self.beam_var(p.id)).collect();
1292 let patterns = func
1293 .params
1294 .iter()
1295 .map(|p| BeamPattern::Var(p.clone()))
1296 .collect();
1297 let body = self.emit_expr(&decl.body);
1298 func.add_clause(BeamClause {
1299 pattern: BeamPattern::Tuple(patterns),
1300 guard: None,
1301 body,
1302 });
1303 self.lower_function(&mut func);
1304 func
1305 }
1306 pub(super) fn lower_function(&mut self, func: &mut BeamFunction) {
1308 let entry = self.fresh_label();
1309 func.instrs.push(BeamInstr::Label(entry));
1310 func.instrs.push(BeamInstr::FuncInfo {
1311 module: self.module.name.clone(),
1312 function: func.name.clone(),
1313 arity: func.arity as u32,
1314 });
1315 let body_label = self.fresh_label();
1316 func.instrs.push(BeamInstr::Label(body_label));
1317 for (i, _param) in func.params.iter().enumerate() {
1318 func.instrs.push(BeamInstr::Move {
1319 src: BeamReg::X(i as u32),
1320 dst: BeamReg::Y(i as u32),
1321 });
1322 }
1323 func.instrs.push(BeamInstr::Return);
1324 }
1325 pub fn emit_core_erlang(&mut self) -> String {
1327 let mut out = String::new();
1328 out.push_str(&format!("module '{}'\n", self.module.name));
1329 out.push_str(" [");
1330 let exports = self.module.export_list();
1331 for (i, exp) in exports.iter().enumerate() {
1332 if i > 0 {
1333 out.push_str(", ");
1334 }
1335 out.push_str(&format!("'{}'", exp));
1336 }
1337 out.push_str("]\n");
1338 out.push_str(" attributes []\n");
1339 for func in &self.module.functions.clone() {
1340 out.push_str(&self.emit_function_core_erlang(func));
1341 }
1342 out.push_str("end\n");
1343 out
1344 }
1345 pub(super) fn emit_function_core_erlang(&self, func: &BeamFunction) -> String {
1347 let mut out = String::new();
1348 let params_str = func
1349 .params
1350 .iter()
1351 .map(|p| format!("_{}", p))
1352 .collect::<Vec<_>>()
1353 .join(", ");
1354 out.push_str(&format!(
1355 "\n'{}'/{} =\n fun ({}) ->\n",
1356 func.name, func.arity, params_str
1357 ));
1358 out.push_str(" 'ok'\n");
1359 out
1360 }
1361 pub fn emit_asm(&self) -> String {
1363 let mut out = String::new();
1364 out.push_str(&format!("{{module, '{}'}}.\n\n", self.module.name));
1365 out.push_str(&format!(
1366 "{{exports, [{}]}}.\n\n",
1367 self.module
1368 .exports
1369 .iter()
1370 .map(|(n, a)| format!("{{{}, {}}}", n, a))
1371 .collect::<Vec<_>>()
1372 .join(", ")
1373 ));
1374 for func in &self.module.functions {
1375 out.push_str(&format!("%% Function: {}/{}\n", func.name, func.arity));
1376 for instr in &func.instrs {
1377 out.push_str(&format!(" {}\n", emit_instr(instr)));
1378 }
1379 out.push('\n');
1380 }
1381 out
1382 }
1383}
1384#[allow(dead_code)]
1386pub struct BeamPrinter {
1387 pub(super) indent: usize,
1388 pub(super) buf: String,
1389}
1390impl BeamPrinter {
1391 #[allow(dead_code)]
1393 pub fn new() -> Self {
1394 BeamPrinter {
1395 indent: 0,
1396 buf: String::new(),
1397 }
1398 }
1399 pub(super) fn pad(&self) -> String {
1400 " ".repeat(self.indent)
1401 }
1402 pub(super) fn push(&mut self, s: &str) {
1403 self.buf.push_str(s);
1404 }
1405 pub(super) fn newline(&mut self) {
1406 self.buf.push('\n');
1407 self.buf.push_str(&self.pad());
1408 }
1409 #[allow(dead_code)]
1411 pub fn print_expr(&mut self, expr: &BeamExpr) {
1412 match expr {
1413 BeamExpr::LitInt(n) => self.push(&n.to_string()),
1414 BeamExpr::LitFloat(v) => self.push(&format!("{:.6}", v)),
1415 BeamExpr::LitAtom(a) => self.push(&format!("'{}'", a)),
1416 BeamExpr::LitString(s) => self.push(&format!("\"{}\"", s)),
1417 BeamExpr::Nil => self.push("[]"),
1418 BeamExpr::Var(name) => self.push(name),
1419 BeamExpr::Tuple(elems) => {
1420 self.push("{");
1421 for (i, e) in elems.iter().enumerate() {
1422 if i > 0 {
1423 self.push(", ");
1424 }
1425 self.print_expr(e);
1426 }
1427 self.push("}");
1428 }
1429 BeamExpr::Cons(head, tail) => {
1430 self.push("[");
1431 self.print_expr(head);
1432 self.push(" | ");
1433 self.print_expr(tail);
1434 self.push("]");
1435 }
1436 BeamExpr::Let { var, value, body } => {
1437 self.push(&format!("let {} =", var));
1438 self.indent += 1;
1439 self.newline();
1440 self.print_expr(value);
1441 self.indent -= 1;
1442 self.newline();
1443 self.push("in ");
1444 self.print_expr(body);
1445 }
1446 BeamExpr::Apply { fun, args } => {
1447 self.push("apply ");
1448 self.print_expr(fun);
1449 self.push("(");
1450 for (i, a) in args.iter().enumerate() {
1451 if i > 0 {
1452 self.push(", ");
1453 }
1454 self.print_expr(a);
1455 }
1456 self.push(")");
1457 }
1458 BeamExpr::Call { module, func, args } => {
1459 if let Some(m) = module {
1460 self.push(&format!("call '{}':'{}' (", m, func));
1461 } else {
1462 self.push(&format!("call '{}' (", func));
1463 }
1464 for (i, a) in args.iter().enumerate() {
1465 if i > 0 {
1466 self.push(", ");
1467 }
1468 self.print_expr(a);
1469 }
1470 self.push(")");
1471 }
1472 BeamExpr::Primop { name, args } => {
1473 self.push(&format!("primop '{}' (", name));
1474 for (i, a) in args.iter().enumerate() {
1475 if i > 0 {
1476 self.push(", ");
1477 }
1478 self.print_expr(a);
1479 }
1480 self.push(")");
1481 }
1482 _ => self.push("..."),
1483 }
1484 }
1485 #[allow(dead_code)]
1487 pub fn finish(self) -> String {
1488 self.buf
1489 }
1490}
1491#[derive(Debug, Clone)]
1493pub struct BeamModule {
1494 pub name: String,
1496 pub attributes: Vec<(String, String)>,
1498 pub functions: Vec<BeamFunction>,
1500 pub exports: Vec<(String, usize)>,
1502 pub on_load: Option<String>,
1504 pub compile_info: HashMap<String, String>,
1506}
1507impl BeamModule {
1508 pub fn new(name: impl Into<String>) -> Self {
1510 BeamModule {
1511 name: name.into(),
1512 attributes: Vec::new(),
1513 functions: Vec::new(),
1514 exports: Vec::new(),
1515 on_load: None,
1516 compile_info: HashMap::new(),
1517 }
1518 }
1519 pub fn add_function(&mut self, func: BeamFunction) {
1521 if func.exported {
1522 self.exports.push((func.name.clone(), func.arity));
1523 }
1524 self.functions.push(func);
1525 }
1526 pub fn add_attribute(&mut self, key: impl Into<String>, value: impl Into<String>) {
1528 self.attributes.push((key.into(), value.into()));
1529 }
1530 pub fn find_function(&self, name: &str, arity: usize) -> Option<&BeamFunction> {
1532 self.functions
1533 .iter()
1534 .find(|f| f.name == name && f.arity == arity)
1535 }
1536 pub fn export_list(&self) -> Vec<String> {
1538 self.exports
1539 .iter()
1540 .map(|(n, a)| format!("{}/{}", n, a))
1541 .collect()
1542 }
1543}
1544#[derive(Debug, Clone, PartialEq)]
1546pub enum BeamReg {
1547 X(u32),
1549 Y(u32),
1551 FR(u32),
1553}