1use crate::lcnf::*;
6
7use super::functions::*;
8use std::collections::{HashMap, HashSet, VecDeque};
9
10#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct WasmCacheEntry {
13 pub key: String,
14 pub data: Vec<u8>,
15 pub timestamp: u64,
16 pub valid: bool,
17}
18#[allow(dead_code)]
19#[derive(Debug, Clone)]
20pub struct WasmDepGraph {
21 pub(super) nodes: Vec<u32>,
22 pub(super) edges: Vec<(u32, u32)>,
23}
24impl WasmDepGraph {
25 #[allow(dead_code)]
26 pub fn new() -> Self {
27 WasmDepGraph {
28 nodes: Vec::new(),
29 edges: Vec::new(),
30 }
31 }
32 #[allow(dead_code)]
33 pub fn add_node(&mut self, id: u32) {
34 if !self.nodes.contains(&id) {
35 self.nodes.push(id);
36 }
37 }
38 #[allow(dead_code)]
39 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
40 self.add_node(dep);
41 self.add_node(dependent);
42 self.edges.push((dep, dependent));
43 }
44 #[allow(dead_code)]
45 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
46 self.edges
47 .iter()
48 .filter(|(d, _)| *d == node)
49 .map(|(_, dep)| *dep)
50 .collect()
51 }
52 #[allow(dead_code)]
53 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
54 self.edges
55 .iter()
56 .filter(|(_, dep)| *dep == node)
57 .map(|(d, _)| *d)
58 .collect()
59 }
60 #[allow(dead_code)]
61 pub fn topological_sort(&self) -> Vec<u32> {
62 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
63 for &n in &self.nodes {
64 in_degree.insert(n, 0);
65 }
66 for (_, dep) in &self.edges {
67 *in_degree.entry(*dep).or_insert(0) += 1;
68 }
69 let mut queue: std::collections::VecDeque<u32> = self
70 .nodes
71 .iter()
72 .filter(|&&n| in_degree[&n] == 0)
73 .copied()
74 .collect();
75 let mut result = Vec::new();
76 while let Some(node) = queue.pop_front() {
77 result.push(node);
78 for dep in self.dependents_of(node) {
79 let cnt = in_degree.entry(dep).or_insert(0);
80 *cnt = cnt.saturating_sub(1);
81 if *cnt == 0 {
82 queue.push_back(dep);
83 }
84 }
85 }
86 result
87 }
88 #[allow(dead_code)]
89 pub fn has_cycle(&self) -> bool {
90 self.topological_sort().len() < self.nodes.len()
91 }
92}
93#[allow(dead_code)]
95#[derive(Debug, Default)]
96pub struct WasmX2PassRegistry {
97 pub(super) configs: Vec<WasmX2PassConfig>,
98 pub(super) stats: Vec<WasmX2PassStats>,
99}
100impl WasmX2PassRegistry {
101 #[allow(dead_code)]
102 pub fn new() -> Self {
103 Self::default()
104 }
105 #[allow(dead_code)]
106 pub fn register(&mut self, c: WasmX2PassConfig) {
107 self.stats.push(WasmX2PassStats::new());
108 self.configs.push(c);
109 }
110 #[allow(dead_code)]
111 pub fn len(&self) -> usize {
112 self.configs.len()
113 }
114 #[allow(dead_code)]
115 pub fn is_empty(&self) -> bool {
116 self.configs.is_empty()
117 }
118 #[allow(dead_code)]
119 pub fn get(&self, i: usize) -> Option<&WasmX2PassConfig> {
120 self.configs.get(i)
121 }
122 #[allow(dead_code)]
123 pub fn get_stats(&self, i: usize) -> Option<&WasmX2PassStats> {
124 self.stats.get(i)
125 }
126 #[allow(dead_code)]
127 pub fn enabled_passes(&self) -> Vec<&WasmX2PassConfig> {
128 self.configs.iter().filter(|c| c.enabled).collect()
129 }
130 #[allow(dead_code)]
131 pub fn passes_in_phase(&self, ph: &WasmX2PassPhase) -> Vec<&WasmX2PassConfig> {
132 self.configs
133 .iter()
134 .filter(|c| c.enabled && &c.phase == ph)
135 .collect()
136 }
137 #[allow(dead_code)]
138 pub fn total_nodes_visited(&self) -> usize {
139 self.stats.iter().map(|s| s.nodes_visited).sum()
140 }
141 #[allow(dead_code)]
142 pub fn any_changed(&self) -> bool {
143 self.stats.iter().any(|s| s.changed)
144 }
145}
146#[allow(dead_code)]
148#[derive(Debug, Clone)]
149pub struct WasmExtDomTree {
150 pub(super) idom: Vec<Option<usize>>,
151 pub(super) children: Vec<Vec<usize>>,
152 pub(super) depth: Vec<usize>,
153}
154impl WasmExtDomTree {
155 #[allow(dead_code)]
156 pub fn new(n: usize) -> Self {
157 Self {
158 idom: vec![None; n],
159 children: vec![Vec::new(); n],
160 depth: vec![0; n],
161 }
162 }
163 #[allow(dead_code)]
164 pub fn set_idom(&mut self, node: usize, dom: usize) {
165 if node < self.idom.len() {
166 self.idom[node] = Some(dom);
167 if dom < self.children.len() {
168 self.children[dom].push(node);
169 }
170 self.depth[node] = if dom < self.depth.len() {
171 self.depth[dom] + 1
172 } else {
173 1
174 };
175 }
176 }
177 #[allow(dead_code)]
178 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
179 if a == b {
180 return true;
181 }
182 let n = self.idom.len();
183 for _ in 0..n {
184 match self.idom.get(b).copied().flatten() {
185 None => return false,
186 Some(p) if p == a => return true,
187 Some(p) if p == b => return false,
188 Some(p) => b = p,
189 }
190 }
191 false
192 }
193 #[allow(dead_code)]
194 pub fn children_of(&self, n: usize) -> &[usize] {
195 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
196 }
197 #[allow(dead_code)]
198 pub fn depth_of(&self, n: usize) -> usize {
199 self.depth.get(n).copied().unwrap_or(0)
200 }
201 #[allow(dead_code)]
202 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
203 let n = self.idom.len();
204 for _ in 0..(2 * n) {
205 if a == b {
206 return a;
207 }
208 if self.depth_of(a) > self.depth_of(b) {
209 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
210 } else {
211 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
212 }
213 }
214 0
215 }
216}
217#[derive(Debug, Clone, Default)]
219pub struct WasmModule {
220 pub imports: Vec<WasmImport>,
222 pub functions: Vec<WasmFunc>,
224 pub exports: Vec<String>,
226 pub memory: Option<u32>,
228 pub globals: Vec<WasmGlobal>,
230}
231impl WasmModule {
232 pub fn new() -> Self {
234 Self::default()
235 }
236 pub fn to_wat(&self) -> String {
238 let mut out = String::from("(module\n");
239 for import in &self.imports {
240 out.push_str(&format!("{}\n", import));
241 }
242 if let Some(pages) = self.memory {
243 out.push_str(&format!(" (memory {})\n", pages));
244 }
245 for global in &self.globals {
246 out.push_str(&format!("{}\n", global));
247 }
248 for func in &self.functions {
249 out.push_str(&format!("{}\n", func));
250 }
251 for export in &self.exports {
252 out.push_str(&format!(" (export \"{}\" (func ${}))\n", export, export));
253 }
254 out.push(')');
255 out
256 }
257}
258#[allow(dead_code)]
259#[derive(Debug, Clone)]
260pub struct WasmPassConfig {
261 pub phase: WasmPassPhase,
262 pub enabled: bool,
263 pub max_iterations: u32,
264 pub debug_output: bool,
265 pub pass_name: String,
266}
267impl WasmPassConfig {
268 #[allow(dead_code)]
269 pub fn new(name: impl Into<String>, phase: WasmPassPhase) -> Self {
270 WasmPassConfig {
271 phase,
272 enabled: true,
273 max_iterations: 10,
274 debug_output: false,
275 pass_name: name.into(),
276 }
277 }
278 #[allow(dead_code)]
279 pub fn disabled(mut self) -> Self {
280 self.enabled = false;
281 self
282 }
283 #[allow(dead_code)]
284 pub fn with_debug(mut self) -> Self {
285 self.debug_output = true;
286 self
287 }
288 #[allow(dead_code)]
289 pub fn max_iter(mut self, n: u32) -> Self {
290 self.max_iterations = n;
291 self
292 }
293}
294#[allow(dead_code)]
295#[derive(Debug, Clone)]
296pub struct WasmWorklist {
297 pub(super) items: std::collections::VecDeque<u32>,
298 pub(super) in_worklist: std::collections::HashSet<u32>,
299}
300impl WasmWorklist {
301 #[allow(dead_code)]
302 pub fn new() -> Self {
303 WasmWorklist {
304 items: std::collections::VecDeque::new(),
305 in_worklist: std::collections::HashSet::new(),
306 }
307 }
308 #[allow(dead_code)]
309 pub fn push(&mut self, item: u32) -> bool {
310 if self.in_worklist.insert(item) {
311 self.items.push_back(item);
312 true
313 } else {
314 false
315 }
316 }
317 #[allow(dead_code)]
318 pub fn pop(&mut self) -> Option<u32> {
319 let item = self.items.pop_front()?;
320 self.in_worklist.remove(&item);
321 Some(item)
322 }
323 #[allow(dead_code)]
324 pub fn is_empty(&self) -> bool {
325 self.items.is_empty()
326 }
327 #[allow(dead_code)]
328 pub fn len(&self) -> usize {
329 self.items.len()
330 }
331 #[allow(dead_code)]
332 pub fn contains(&self, item: u32) -> bool {
333 self.in_worklist.contains(&item)
334 }
335}
336#[allow(dead_code)]
337#[derive(Debug, Clone)]
338pub struct WasmDominatorTree {
339 pub idom: Vec<Option<u32>>,
340 pub dom_children: Vec<Vec<u32>>,
341 pub dom_depth: Vec<u32>,
342}
343impl WasmDominatorTree {
344 #[allow(dead_code)]
345 pub fn new(size: usize) -> Self {
346 WasmDominatorTree {
347 idom: vec![None; size],
348 dom_children: vec![Vec::new(); size],
349 dom_depth: vec![0; size],
350 }
351 }
352 #[allow(dead_code)]
353 pub fn set_idom(&mut self, node: usize, idom: u32) {
354 self.idom[node] = Some(idom);
355 }
356 #[allow(dead_code)]
357 pub fn dominates(&self, a: usize, b: usize) -> bool {
358 if a == b {
359 return true;
360 }
361 let mut cur = b;
362 loop {
363 match self.idom[cur] {
364 Some(parent) if parent as usize == a => return true,
365 Some(parent) if parent as usize == cur => return false,
366 Some(parent) => cur = parent as usize,
367 None => return false,
368 }
369 }
370 }
371 #[allow(dead_code)]
372 pub fn depth(&self, node: usize) -> u32 {
373 self.dom_depth.get(node).copied().unwrap_or(0)
374 }
375}
376#[allow(dead_code)]
378#[derive(Debug, Clone)]
379pub struct WasmExtDepGraph {
380 pub(super) n: usize,
381 pub(super) adj: Vec<Vec<usize>>,
382 pub(super) rev: Vec<Vec<usize>>,
383 pub(super) edge_count: usize,
384}
385impl WasmExtDepGraph {
386 #[allow(dead_code)]
387 pub fn new(n: usize) -> Self {
388 Self {
389 n,
390 adj: vec![Vec::new(); n],
391 rev: vec![Vec::new(); n],
392 edge_count: 0,
393 }
394 }
395 #[allow(dead_code)]
396 pub fn add_edge(&mut self, from: usize, to: usize) {
397 if from < self.n && to < self.n {
398 if !self.adj[from].contains(&to) {
399 self.adj[from].push(to);
400 self.rev[to].push(from);
401 self.edge_count += 1;
402 }
403 }
404 }
405 #[allow(dead_code)]
406 pub fn succs(&self, n: usize) -> &[usize] {
407 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
408 }
409 #[allow(dead_code)]
410 pub fn preds(&self, n: usize) -> &[usize] {
411 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
412 }
413 #[allow(dead_code)]
414 pub fn topo_sort(&self) -> Option<Vec<usize>> {
415 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
416 let mut q: std::collections::VecDeque<usize> =
417 (0..self.n).filter(|&i| deg[i] == 0).collect();
418 let mut out = Vec::with_capacity(self.n);
419 while let Some(u) = q.pop_front() {
420 out.push(u);
421 for &v in &self.adj[u] {
422 deg[v] -= 1;
423 if deg[v] == 0 {
424 q.push_back(v);
425 }
426 }
427 }
428 if out.len() == self.n {
429 Some(out)
430 } else {
431 None
432 }
433 }
434 #[allow(dead_code)]
435 pub fn has_cycle(&self) -> bool {
436 self.topo_sort().is_none()
437 }
438 #[allow(dead_code)]
439 pub fn reachable(&self, start: usize) -> Vec<usize> {
440 let mut vis = vec![false; self.n];
441 let mut stk = vec![start];
442 let mut out = Vec::new();
443 while let Some(u) = stk.pop() {
444 if u < self.n && !vis[u] {
445 vis[u] = true;
446 out.push(u);
447 for &v in &self.adj[u] {
448 if !vis[v] {
449 stk.push(v);
450 }
451 }
452 }
453 }
454 out
455 }
456 #[allow(dead_code)]
457 pub fn scc(&self) -> Vec<Vec<usize>> {
458 let mut visited = vec![false; self.n];
459 let mut order = Vec::new();
460 for i in 0..self.n {
461 if !visited[i] {
462 let mut stk = vec![(i, 0usize)];
463 while let Some((u, idx)) = stk.last_mut() {
464 if !visited[*u] {
465 visited[*u] = true;
466 }
467 if *idx < self.adj[*u].len() {
468 let v = self.adj[*u][*idx];
469 *idx += 1;
470 if !visited[v] {
471 stk.push((v, 0));
472 }
473 } else {
474 order.push(*u);
475 stk.pop();
476 }
477 }
478 }
479 }
480 let mut comp = vec![usize::MAX; self.n];
481 let mut components: Vec<Vec<usize>> = Vec::new();
482 for &start in order.iter().rev() {
483 if comp[start] == usize::MAX {
484 let cid = components.len();
485 let mut component = Vec::new();
486 let mut stk = vec![start];
487 while let Some(u) = stk.pop() {
488 if comp[u] == usize::MAX {
489 comp[u] = cid;
490 component.push(u);
491 for &v in &self.rev[u] {
492 if comp[v] == usize::MAX {
493 stk.push(v);
494 }
495 }
496 }
497 }
498 components.push(component);
499 }
500 }
501 components
502 }
503 #[allow(dead_code)]
504 pub fn node_count(&self) -> usize {
505 self.n
506 }
507 #[allow(dead_code)]
508 pub fn edge_count(&self) -> usize {
509 self.edge_count
510 }
511}
512#[allow(dead_code)]
514#[derive(Debug, Clone, Default)]
515pub struct WasmX2PassStats {
516 pub iterations: usize,
517 pub changed: bool,
518 pub nodes_visited: usize,
519 pub nodes_modified: usize,
520 pub time_ms: u64,
521 pub memory_bytes: usize,
522 pub errors: usize,
523}
524impl WasmX2PassStats {
525 #[allow(dead_code)]
526 pub fn new() -> Self {
527 Self::default()
528 }
529 #[allow(dead_code)]
530 pub fn visit(&mut self) {
531 self.nodes_visited += 1;
532 }
533 #[allow(dead_code)]
534 pub fn modify(&mut self) {
535 self.nodes_modified += 1;
536 self.changed = true;
537 }
538 #[allow(dead_code)]
539 pub fn iterate(&mut self) {
540 self.iterations += 1;
541 }
542 #[allow(dead_code)]
543 pub fn error(&mut self) {
544 self.errors += 1;
545 }
546 #[allow(dead_code)]
547 pub fn efficiency(&self) -> f64 {
548 if self.nodes_visited == 0 {
549 0.0
550 } else {
551 self.nodes_modified as f64 / self.nodes_visited as f64
552 }
553 }
554 #[allow(dead_code)]
555 pub fn merge(&mut self, o: &WasmX2PassStats) {
556 self.iterations += o.iterations;
557 self.changed |= o.changed;
558 self.nodes_visited += o.nodes_visited;
559 self.nodes_modified += o.nodes_modified;
560 self.time_ms += o.time_ms;
561 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
562 self.errors += o.errors;
563 }
564}
565#[allow(dead_code)]
567#[derive(Debug, Clone)]
568pub struct WasmExtWorklist {
569 pub(super) items: std::collections::VecDeque<usize>,
570 pub(super) present: Vec<bool>,
571}
572impl WasmExtWorklist {
573 #[allow(dead_code)]
574 pub fn new(capacity: usize) -> Self {
575 Self {
576 items: std::collections::VecDeque::new(),
577 present: vec![false; capacity],
578 }
579 }
580 #[allow(dead_code)]
581 pub fn push(&mut self, id: usize) {
582 if id < self.present.len() && !self.present[id] {
583 self.present[id] = true;
584 self.items.push_back(id);
585 }
586 }
587 #[allow(dead_code)]
588 pub fn push_front(&mut self, id: usize) {
589 if id < self.present.len() && !self.present[id] {
590 self.present[id] = true;
591 self.items.push_front(id);
592 }
593 }
594 #[allow(dead_code)]
595 pub fn pop(&mut self) -> Option<usize> {
596 let id = self.items.pop_front()?;
597 if id < self.present.len() {
598 self.present[id] = false;
599 }
600 Some(id)
601 }
602 #[allow(dead_code)]
603 pub fn is_empty(&self) -> bool {
604 self.items.is_empty()
605 }
606 #[allow(dead_code)]
607 pub fn len(&self) -> usize {
608 self.items.len()
609 }
610 #[allow(dead_code)]
611 pub fn contains(&self, id: usize) -> bool {
612 id < self.present.len() && self.present[id]
613 }
614 #[allow(dead_code)]
615 pub fn drain_all(&mut self) -> Vec<usize> {
616 let v: Vec<usize> = self.items.drain(..).collect();
617 for &id in &v {
618 if id < self.present.len() {
619 self.present[id] = false;
620 }
621 }
622 v
623 }
624}
625#[allow(dead_code)]
627#[derive(Debug)]
628pub struct WasmExtCache {
629 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
630 pub(super) cap: usize,
631 pub(super) total_hits: u64,
632 pub(super) total_misses: u64,
633}
634impl WasmExtCache {
635 #[allow(dead_code)]
636 pub fn new(cap: usize) -> Self {
637 Self {
638 entries: Vec::new(),
639 cap,
640 total_hits: 0,
641 total_misses: 0,
642 }
643 }
644 #[allow(dead_code)]
645 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
646 for e in self.entries.iter_mut() {
647 if e.0 == key && e.2 {
648 e.3 += 1;
649 self.total_hits += 1;
650 return Some(&e.1);
651 }
652 }
653 self.total_misses += 1;
654 None
655 }
656 #[allow(dead_code)]
657 pub fn put(&mut self, key: u64, data: Vec<u8>) {
658 if self.entries.len() >= self.cap {
659 self.entries.retain(|e| e.2);
660 if self.entries.len() >= self.cap {
661 self.entries.remove(0);
662 }
663 }
664 self.entries.push((key, data, true, 0));
665 }
666 #[allow(dead_code)]
667 pub fn invalidate(&mut self) {
668 for e in self.entries.iter_mut() {
669 e.2 = false;
670 }
671 }
672 #[allow(dead_code)]
673 pub fn hit_rate(&self) -> f64 {
674 let t = self.total_hits + self.total_misses;
675 if t == 0 {
676 0.0
677 } else {
678 self.total_hits as f64 / t as f64
679 }
680 }
681 #[allow(dead_code)]
682 pub fn live_count(&self) -> usize {
683 self.entries.iter().filter(|e| e.2).count()
684 }
685}
686#[allow(dead_code)]
688#[derive(Debug, Clone)]
689pub struct WasmX2DomTree {
690 pub(super) idom: Vec<Option<usize>>,
691 pub(super) children: Vec<Vec<usize>>,
692 pub(super) depth: Vec<usize>,
693}
694impl WasmX2DomTree {
695 #[allow(dead_code)]
696 pub fn new(n: usize) -> Self {
697 Self {
698 idom: vec![None; n],
699 children: vec![Vec::new(); n],
700 depth: vec![0; n],
701 }
702 }
703 #[allow(dead_code)]
704 pub fn set_idom(&mut self, node: usize, dom: usize) {
705 if node < self.idom.len() {
706 self.idom[node] = Some(dom);
707 if dom < self.children.len() {
708 self.children[dom].push(node);
709 }
710 self.depth[node] = if dom < self.depth.len() {
711 self.depth[dom] + 1
712 } else {
713 1
714 };
715 }
716 }
717 #[allow(dead_code)]
718 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
719 if a == b {
720 return true;
721 }
722 let n = self.idom.len();
723 for _ in 0..n {
724 match self.idom.get(b).copied().flatten() {
725 None => return false,
726 Some(p) if p == a => return true,
727 Some(p) if p == b => return false,
728 Some(p) => b = p,
729 }
730 }
731 false
732 }
733 #[allow(dead_code)]
734 pub fn children_of(&self, n: usize) -> &[usize] {
735 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
736 }
737 #[allow(dead_code)]
738 pub fn depth_of(&self, n: usize) -> usize {
739 self.depth.get(n).copied().unwrap_or(0)
740 }
741 #[allow(dead_code)]
742 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
743 let n = self.idom.len();
744 for _ in 0..(2 * n) {
745 if a == b {
746 return a;
747 }
748 if self.depth_of(a) > self.depth_of(b) {
749 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
750 } else {
751 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
752 }
753 }
754 0
755 }
756}
757#[derive(Debug, Clone, PartialEq)]
759pub enum WasmImportKind {
760 Func {
762 params: Vec<WasmType>,
763 results: Vec<WasmType>,
764 },
765 Memory { min_pages: u32 },
767 Global { ty: WasmType, mutable: bool },
769}
770#[allow(dead_code)]
772#[derive(Debug, Clone)]
773pub struct WasmX2DepGraph {
774 pub(super) n: usize,
775 pub(super) adj: Vec<Vec<usize>>,
776 pub(super) rev: Vec<Vec<usize>>,
777 pub(super) edge_count: usize,
778}
779impl WasmX2DepGraph {
780 #[allow(dead_code)]
781 pub fn new(n: usize) -> Self {
782 Self {
783 n,
784 adj: vec![Vec::new(); n],
785 rev: vec![Vec::new(); n],
786 edge_count: 0,
787 }
788 }
789 #[allow(dead_code)]
790 pub fn add_edge(&mut self, from: usize, to: usize) {
791 if from < self.n && to < self.n {
792 if !self.adj[from].contains(&to) {
793 self.adj[from].push(to);
794 self.rev[to].push(from);
795 self.edge_count += 1;
796 }
797 }
798 }
799 #[allow(dead_code)]
800 pub fn succs(&self, n: usize) -> &[usize] {
801 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
802 }
803 #[allow(dead_code)]
804 pub fn preds(&self, n: usize) -> &[usize] {
805 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
806 }
807 #[allow(dead_code)]
808 pub fn topo_sort(&self) -> Option<Vec<usize>> {
809 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
810 let mut q: std::collections::VecDeque<usize> =
811 (0..self.n).filter(|&i| deg[i] == 0).collect();
812 let mut out = Vec::with_capacity(self.n);
813 while let Some(u) = q.pop_front() {
814 out.push(u);
815 for &v in &self.adj[u] {
816 deg[v] -= 1;
817 if deg[v] == 0 {
818 q.push_back(v);
819 }
820 }
821 }
822 if out.len() == self.n {
823 Some(out)
824 } else {
825 None
826 }
827 }
828 #[allow(dead_code)]
829 pub fn has_cycle(&self) -> bool {
830 self.topo_sort().is_none()
831 }
832 #[allow(dead_code)]
833 pub fn reachable(&self, start: usize) -> Vec<usize> {
834 let mut vis = vec![false; self.n];
835 let mut stk = vec![start];
836 let mut out = Vec::new();
837 while let Some(u) = stk.pop() {
838 if u < self.n && !vis[u] {
839 vis[u] = true;
840 out.push(u);
841 for &v in &self.adj[u] {
842 if !vis[v] {
843 stk.push(v);
844 }
845 }
846 }
847 }
848 out
849 }
850 #[allow(dead_code)]
851 pub fn scc(&self) -> Vec<Vec<usize>> {
852 let mut visited = vec![false; self.n];
853 let mut order = Vec::new();
854 for i in 0..self.n {
855 if !visited[i] {
856 let mut stk = vec![(i, 0usize)];
857 while let Some((u, idx)) = stk.last_mut() {
858 if !visited[*u] {
859 visited[*u] = true;
860 }
861 if *idx < self.adj[*u].len() {
862 let v = self.adj[*u][*idx];
863 *idx += 1;
864 if !visited[v] {
865 stk.push((v, 0));
866 }
867 } else {
868 order.push(*u);
869 stk.pop();
870 }
871 }
872 }
873 }
874 let mut comp = vec![usize::MAX; self.n];
875 let mut components: Vec<Vec<usize>> = Vec::new();
876 for &start in order.iter().rev() {
877 if comp[start] == usize::MAX {
878 let cid = components.len();
879 let mut component = Vec::new();
880 let mut stk = vec![start];
881 while let Some(u) = stk.pop() {
882 if comp[u] == usize::MAX {
883 comp[u] = cid;
884 component.push(u);
885 for &v in &self.rev[u] {
886 if comp[v] == usize::MAX {
887 stk.push(v);
888 }
889 }
890 }
891 }
892 components.push(component);
893 }
894 }
895 components
896 }
897 #[allow(dead_code)]
898 pub fn node_count(&self) -> usize {
899 self.n
900 }
901 #[allow(dead_code)]
902 pub fn edge_count(&self) -> usize {
903 self.edge_count
904 }
905}
906#[allow(dead_code)]
907#[derive(Debug, Clone)]
908pub struct WasmLivenessInfo {
909 pub live_in: Vec<std::collections::HashSet<u32>>,
910 pub live_out: Vec<std::collections::HashSet<u32>>,
911 pub defs: Vec<std::collections::HashSet<u32>>,
912 pub uses: Vec<std::collections::HashSet<u32>>,
913}
914impl WasmLivenessInfo {
915 #[allow(dead_code)]
916 pub fn new(block_count: usize) -> Self {
917 WasmLivenessInfo {
918 live_in: vec![std::collections::HashSet::new(); block_count],
919 live_out: vec![std::collections::HashSet::new(); block_count],
920 defs: vec![std::collections::HashSet::new(); block_count],
921 uses: vec![std::collections::HashSet::new(); block_count],
922 }
923 }
924 #[allow(dead_code)]
925 pub fn add_def(&mut self, block: usize, var: u32) {
926 if block < self.defs.len() {
927 self.defs[block].insert(var);
928 }
929 }
930 #[allow(dead_code)]
931 pub fn add_use(&mut self, block: usize, var: u32) {
932 if block < self.uses.len() {
933 self.uses[block].insert(var);
934 }
935 }
936 #[allow(dead_code)]
937 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
938 self.live_in
939 .get(block)
940 .map(|s| s.contains(&var))
941 .unwrap_or(false)
942 }
943 #[allow(dead_code)]
944 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
945 self.live_out
946 .get(block)
947 .map(|s| s.contains(&var))
948 .unwrap_or(false)
949 }
950}
951#[allow(dead_code)]
953#[derive(Debug, Clone, PartialEq, Eq, Hash)]
954pub enum WasmX2PassPhase {
955 Early,
956 Middle,
957 Late,
958 Finalize,
959}
960impl WasmX2PassPhase {
961 #[allow(dead_code)]
962 pub fn is_early(&self) -> bool {
963 matches!(self, Self::Early)
964 }
965 #[allow(dead_code)]
966 pub fn is_middle(&self) -> bool {
967 matches!(self, Self::Middle)
968 }
969 #[allow(dead_code)]
970 pub fn is_late(&self) -> bool {
971 matches!(self, Self::Late)
972 }
973 #[allow(dead_code)]
974 pub fn is_finalize(&self) -> bool {
975 matches!(self, Self::Finalize)
976 }
977 #[allow(dead_code)]
978 pub fn order(&self) -> u32 {
979 match self {
980 Self::Early => 0,
981 Self::Middle => 1,
982 Self::Late => 2,
983 Self::Finalize => 3,
984 }
985 }
986 #[allow(dead_code)]
987 pub fn from_order(n: u32) -> Option<Self> {
988 match n {
989 0 => Some(Self::Early),
990 1 => Some(Self::Middle),
991 2 => Some(Self::Late),
992 3 => Some(Self::Finalize),
993 _ => None,
994 }
995 }
996}
997#[allow(dead_code)]
999#[derive(Debug, Clone)]
1000pub struct WasmX2PassConfig {
1001 pub name: String,
1002 pub phase: WasmX2PassPhase,
1003 pub enabled: bool,
1004 pub max_iterations: usize,
1005 pub debug: u32,
1006 pub timeout_ms: Option<u64>,
1007}
1008impl WasmX2PassConfig {
1009 #[allow(dead_code)]
1010 pub fn new(name: impl Into<String>) -> Self {
1011 Self {
1012 name: name.into(),
1013 phase: WasmX2PassPhase::Middle,
1014 enabled: true,
1015 max_iterations: 100,
1016 debug: 0,
1017 timeout_ms: None,
1018 }
1019 }
1020 #[allow(dead_code)]
1021 pub fn with_phase(mut self, phase: WasmX2PassPhase) -> Self {
1022 self.phase = phase;
1023 self
1024 }
1025 #[allow(dead_code)]
1026 pub fn with_max_iter(mut self, n: usize) -> Self {
1027 self.max_iterations = n;
1028 self
1029 }
1030 #[allow(dead_code)]
1031 pub fn with_debug(mut self, d: u32) -> Self {
1032 self.debug = d;
1033 self
1034 }
1035 #[allow(dead_code)]
1036 pub fn disabled(mut self) -> Self {
1037 self.enabled = false;
1038 self
1039 }
1040 #[allow(dead_code)]
1041 pub fn with_timeout(mut self, ms: u64) -> Self {
1042 self.timeout_ms = Some(ms);
1043 self
1044 }
1045 #[allow(dead_code)]
1046 pub fn is_debug_enabled(&self) -> bool {
1047 self.debug > 0
1048 }
1049}
1050#[allow(dead_code)]
1051#[derive(Debug, Clone, Default)]
1052pub struct WasmPassStats {
1053 pub total_runs: u32,
1054 pub successful_runs: u32,
1055 pub total_changes: u64,
1056 pub time_ms: u64,
1057 pub iterations_used: u32,
1058}
1059impl WasmPassStats {
1060 #[allow(dead_code)]
1061 pub fn new() -> Self {
1062 Self::default()
1063 }
1064 #[allow(dead_code)]
1065 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1066 self.total_runs += 1;
1067 self.successful_runs += 1;
1068 self.total_changes += changes;
1069 self.time_ms += time_ms;
1070 self.iterations_used = iterations;
1071 }
1072 #[allow(dead_code)]
1073 pub fn average_changes_per_run(&self) -> f64 {
1074 if self.total_runs == 0 {
1075 return 0.0;
1076 }
1077 self.total_changes as f64 / self.total_runs as f64
1078 }
1079 #[allow(dead_code)]
1080 pub fn success_rate(&self) -> f64 {
1081 if self.total_runs == 0 {
1082 return 0.0;
1083 }
1084 self.successful_runs as f64 / self.total_runs as f64
1085 }
1086 #[allow(dead_code)]
1087 pub fn format_summary(&self) -> String {
1088 format!(
1089 "Runs: {}/{}, Changes: {}, Time: {}ms",
1090 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1091 )
1092 }
1093}
1094#[derive(Debug, Clone, PartialEq)]
1096pub struct WasmGlobal {
1097 pub name: String,
1099 pub ty: WasmType,
1101 pub mutable: bool,
1103 pub init_value: String,
1105}
1106#[allow(dead_code)]
1108#[derive(Debug, Clone)]
1109pub struct WasmExtPassConfig {
1110 pub name: String,
1111 pub phase: WasmExtPassPhase,
1112 pub enabled: bool,
1113 pub max_iterations: usize,
1114 pub debug: u32,
1115 pub timeout_ms: Option<u64>,
1116}
1117impl WasmExtPassConfig {
1118 #[allow(dead_code)]
1119 pub fn new(name: impl Into<String>) -> Self {
1120 Self {
1121 name: name.into(),
1122 phase: WasmExtPassPhase::Middle,
1123 enabled: true,
1124 max_iterations: 100,
1125 debug: 0,
1126 timeout_ms: None,
1127 }
1128 }
1129 #[allow(dead_code)]
1130 pub fn with_phase(mut self, phase: WasmExtPassPhase) -> Self {
1131 self.phase = phase;
1132 self
1133 }
1134 #[allow(dead_code)]
1135 pub fn with_max_iter(mut self, n: usize) -> Self {
1136 self.max_iterations = n;
1137 self
1138 }
1139 #[allow(dead_code)]
1140 pub fn with_debug(mut self, d: u32) -> Self {
1141 self.debug = d;
1142 self
1143 }
1144 #[allow(dead_code)]
1145 pub fn disabled(mut self) -> Self {
1146 self.enabled = false;
1147 self
1148 }
1149 #[allow(dead_code)]
1150 pub fn with_timeout(mut self, ms: u64) -> Self {
1151 self.timeout_ms = Some(ms);
1152 self
1153 }
1154 #[allow(dead_code)]
1155 pub fn is_debug_enabled(&self) -> bool {
1156 self.debug > 0
1157 }
1158}
1159#[allow(dead_code)]
1161#[derive(Debug, Clone, Default)]
1162pub struct WasmExtPassStats {
1163 pub iterations: usize,
1164 pub changed: bool,
1165 pub nodes_visited: usize,
1166 pub nodes_modified: usize,
1167 pub time_ms: u64,
1168 pub memory_bytes: usize,
1169 pub errors: usize,
1170}
1171impl WasmExtPassStats {
1172 #[allow(dead_code)]
1173 pub fn new() -> Self {
1174 Self::default()
1175 }
1176 #[allow(dead_code)]
1177 pub fn visit(&mut self) {
1178 self.nodes_visited += 1;
1179 }
1180 #[allow(dead_code)]
1181 pub fn modify(&mut self) {
1182 self.nodes_modified += 1;
1183 self.changed = true;
1184 }
1185 #[allow(dead_code)]
1186 pub fn iterate(&mut self) {
1187 self.iterations += 1;
1188 }
1189 #[allow(dead_code)]
1190 pub fn error(&mut self) {
1191 self.errors += 1;
1192 }
1193 #[allow(dead_code)]
1194 pub fn efficiency(&self) -> f64 {
1195 if self.nodes_visited == 0 {
1196 0.0
1197 } else {
1198 self.nodes_modified as f64 / self.nodes_visited as f64
1199 }
1200 }
1201 #[allow(dead_code)]
1202 pub fn merge(&mut self, o: &WasmExtPassStats) {
1203 self.iterations += o.iterations;
1204 self.changed |= o.changed;
1205 self.nodes_visited += o.nodes_visited;
1206 self.nodes_modified += o.nodes_modified;
1207 self.time_ms += o.time_ms;
1208 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1209 self.errors += o.errors;
1210 }
1211}
1212#[allow(dead_code)]
1213#[derive(Debug, Clone)]
1214pub struct WasmAnalysisCache {
1215 pub(super) entries: std::collections::HashMap<String, WasmCacheEntry>,
1216 pub(super) max_size: usize,
1217 pub(super) hits: u64,
1218 pub(super) misses: u64,
1219}
1220impl WasmAnalysisCache {
1221 #[allow(dead_code)]
1222 pub fn new(max_size: usize) -> Self {
1223 WasmAnalysisCache {
1224 entries: std::collections::HashMap::new(),
1225 max_size,
1226 hits: 0,
1227 misses: 0,
1228 }
1229 }
1230 #[allow(dead_code)]
1231 pub fn get(&mut self, key: &str) -> Option<&WasmCacheEntry> {
1232 if self.entries.contains_key(key) {
1233 self.hits += 1;
1234 self.entries.get(key)
1235 } else {
1236 self.misses += 1;
1237 None
1238 }
1239 }
1240 #[allow(dead_code)]
1241 pub fn insert(&mut self, key: String, data: Vec<u8>) {
1242 if self.entries.len() >= self.max_size {
1243 if let Some(oldest) = self.entries.keys().next().cloned() {
1244 self.entries.remove(&oldest);
1245 }
1246 }
1247 self.entries.insert(
1248 key.clone(),
1249 WasmCacheEntry {
1250 key,
1251 data,
1252 timestamp: 0,
1253 valid: true,
1254 },
1255 );
1256 }
1257 #[allow(dead_code)]
1258 pub fn invalidate(&mut self, key: &str) {
1259 if let Some(entry) = self.entries.get_mut(key) {
1260 entry.valid = false;
1261 }
1262 }
1263 #[allow(dead_code)]
1264 pub fn clear(&mut self) {
1265 self.entries.clear();
1266 }
1267 #[allow(dead_code)]
1268 pub fn hit_rate(&self) -> f64 {
1269 let total = self.hits + self.misses;
1270 if total == 0 {
1271 return 0.0;
1272 }
1273 self.hits as f64 / total as f64
1274 }
1275 #[allow(dead_code)]
1276 pub fn size(&self) -> usize {
1277 self.entries.len()
1278 }
1279}
1280#[allow(dead_code)]
1282#[derive(Debug, Default)]
1283pub struct WasmExtPassRegistry {
1284 pub(super) configs: Vec<WasmExtPassConfig>,
1285 pub(super) stats: Vec<WasmExtPassStats>,
1286}
1287impl WasmExtPassRegistry {
1288 #[allow(dead_code)]
1289 pub fn new() -> Self {
1290 Self::default()
1291 }
1292 #[allow(dead_code)]
1293 pub fn register(&mut self, c: WasmExtPassConfig) {
1294 self.stats.push(WasmExtPassStats::new());
1295 self.configs.push(c);
1296 }
1297 #[allow(dead_code)]
1298 pub fn len(&self) -> usize {
1299 self.configs.len()
1300 }
1301 #[allow(dead_code)]
1302 pub fn is_empty(&self) -> bool {
1303 self.configs.is_empty()
1304 }
1305 #[allow(dead_code)]
1306 pub fn get(&self, i: usize) -> Option<&WasmExtPassConfig> {
1307 self.configs.get(i)
1308 }
1309 #[allow(dead_code)]
1310 pub fn get_stats(&self, i: usize) -> Option<&WasmExtPassStats> {
1311 self.stats.get(i)
1312 }
1313 #[allow(dead_code)]
1314 pub fn enabled_passes(&self) -> Vec<&WasmExtPassConfig> {
1315 self.configs.iter().filter(|c| c.enabled).collect()
1316 }
1317 #[allow(dead_code)]
1318 pub fn passes_in_phase(&self, ph: &WasmExtPassPhase) -> Vec<&WasmExtPassConfig> {
1319 self.configs
1320 .iter()
1321 .filter(|c| c.enabled && &c.phase == ph)
1322 .collect()
1323 }
1324 #[allow(dead_code)]
1325 pub fn total_nodes_visited(&self) -> usize {
1326 self.stats.iter().map(|s| s.nodes_visited).sum()
1327 }
1328 #[allow(dead_code)]
1329 pub fn any_changed(&self) -> bool {
1330 self.stats.iter().any(|s| s.changed)
1331 }
1332}
1333#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1335pub enum WasmType {
1336 I32,
1338 I64,
1340 F32,
1342 F64,
1344 FuncRef,
1346 ExternRef,
1348 V128,
1350}
1351#[allow(dead_code)]
1352pub struct WasmPassRegistry {
1353 pub(super) configs: Vec<WasmPassConfig>,
1354 pub(super) stats: std::collections::HashMap<String, WasmPassStats>,
1355}
1356impl WasmPassRegistry {
1357 #[allow(dead_code)]
1358 pub fn new() -> Self {
1359 WasmPassRegistry {
1360 configs: Vec::new(),
1361 stats: std::collections::HashMap::new(),
1362 }
1363 }
1364 #[allow(dead_code)]
1365 pub fn register(&mut self, config: WasmPassConfig) {
1366 self.stats
1367 .insert(config.pass_name.clone(), WasmPassStats::new());
1368 self.configs.push(config);
1369 }
1370 #[allow(dead_code)]
1371 pub fn enabled_passes(&self) -> Vec<&WasmPassConfig> {
1372 self.configs.iter().filter(|c| c.enabled).collect()
1373 }
1374 #[allow(dead_code)]
1375 pub fn get_stats(&self, name: &str) -> Option<&WasmPassStats> {
1376 self.stats.get(name)
1377 }
1378 #[allow(dead_code)]
1379 pub fn total_passes(&self) -> usize {
1380 self.configs.len()
1381 }
1382 #[allow(dead_code)]
1383 pub fn enabled_count(&self) -> usize {
1384 self.enabled_passes().len()
1385 }
1386 #[allow(dead_code)]
1387 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
1388 if let Some(stats) = self.stats.get_mut(name) {
1389 stats.record_run(changes, time_ms, iter);
1390 }
1391 }
1392}
1393#[allow(dead_code)]
1395#[derive(Debug, Clone)]
1396pub struct WasmX2Worklist {
1397 pub(super) items: std::collections::VecDeque<usize>,
1398 pub(super) present: Vec<bool>,
1399}
1400impl WasmX2Worklist {
1401 #[allow(dead_code)]
1402 pub fn new(capacity: usize) -> Self {
1403 Self {
1404 items: std::collections::VecDeque::new(),
1405 present: vec![false; capacity],
1406 }
1407 }
1408 #[allow(dead_code)]
1409 pub fn push(&mut self, id: usize) {
1410 if id < self.present.len() && !self.present[id] {
1411 self.present[id] = true;
1412 self.items.push_back(id);
1413 }
1414 }
1415 #[allow(dead_code)]
1416 pub fn push_front(&mut self, id: usize) {
1417 if id < self.present.len() && !self.present[id] {
1418 self.present[id] = true;
1419 self.items.push_front(id);
1420 }
1421 }
1422 #[allow(dead_code)]
1423 pub fn pop(&mut self) -> Option<usize> {
1424 let id = self.items.pop_front()?;
1425 if id < self.present.len() {
1426 self.present[id] = false;
1427 }
1428 Some(id)
1429 }
1430 #[allow(dead_code)]
1431 pub fn is_empty(&self) -> bool {
1432 self.items.is_empty()
1433 }
1434 #[allow(dead_code)]
1435 pub fn len(&self) -> usize {
1436 self.items.len()
1437 }
1438 #[allow(dead_code)]
1439 pub fn contains(&self, id: usize) -> bool {
1440 id < self.present.len() && self.present[id]
1441 }
1442 #[allow(dead_code)]
1443 pub fn drain_all(&mut self) -> Vec<usize> {
1444 let v: Vec<usize> = self.items.drain(..).collect();
1445 for &id in &v {
1446 if id < self.present.len() {
1447 self.present[id] = false;
1448 }
1449 }
1450 v
1451 }
1452}
1453#[allow(dead_code)]
1455#[derive(Debug, Clone, Default)]
1456pub struct WasmX2ConstFolder {
1457 pub(super) folds: usize,
1458 pub(super) failures: usize,
1459 pub(super) enabled: bool,
1460}
1461impl WasmX2ConstFolder {
1462 #[allow(dead_code)]
1463 pub fn new() -> Self {
1464 Self {
1465 folds: 0,
1466 failures: 0,
1467 enabled: true,
1468 }
1469 }
1470 #[allow(dead_code)]
1471 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1472 self.folds += 1;
1473 a.checked_add(b)
1474 }
1475 #[allow(dead_code)]
1476 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1477 self.folds += 1;
1478 a.checked_sub(b)
1479 }
1480 #[allow(dead_code)]
1481 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1482 self.folds += 1;
1483 a.checked_mul(b)
1484 }
1485 #[allow(dead_code)]
1486 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1487 if b == 0 {
1488 self.failures += 1;
1489 None
1490 } else {
1491 self.folds += 1;
1492 a.checked_div(b)
1493 }
1494 }
1495 #[allow(dead_code)]
1496 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1497 if b == 0 {
1498 self.failures += 1;
1499 None
1500 } else {
1501 self.folds += 1;
1502 a.checked_rem(b)
1503 }
1504 }
1505 #[allow(dead_code)]
1506 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
1507 self.folds += 1;
1508 a.checked_neg()
1509 }
1510 #[allow(dead_code)]
1511 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1512 if s >= 64 {
1513 self.failures += 1;
1514 None
1515 } else {
1516 self.folds += 1;
1517 a.checked_shl(s)
1518 }
1519 }
1520 #[allow(dead_code)]
1521 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1522 if s >= 64 {
1523 self.failures += 1;
1524 None
1525 } else {
1526 self.folds += 1;
1527 a.checked_shr(s)
1528 }
1529 }
1530 #[allow(dead_code)]
1531 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
1532 self.folds += 1;
1533 a & b
1534 }
1535 #[allow(dead_code)]
1536 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1537 self.folds += 1;
1538 a | b
1539 }
1540 #[allow(dead_code)]
1541 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1542 self.folds += 1;
1543 a ^ b
1544 }
1545 #[allow(dead_code)]
1546 pub fn not_i64(&mut self, a: i64) -> i64 {
1547 self.folds += 1;
1548 !a
1549 }
1550 #[allow(dead_code)]
1551 pub fn fold_count(&self) -> usize {
1552 self.folds
1553 }
1554 #[allow(dead_code)]
1555 pub fn failure_count(&self) -> usize {
1556 self.failures
1557 }
1558 #[allow(dead_code)]
1559 pub fn enable(&mut self) {
1560 self.enabled = true;
1561 }
1562 #[allow(dead_code)]
1563 pub fn disable(&mut self) {
1564 self.enabled = false;
1565 }
1566 #[allow(dead_code)]
1567 pub fn is_enabled(&self) -> bool {
1568 self.enabled
1569 }
1570}
1571#[derive(Debug, Default)]
1575pub struct WasmBackend {
1576 pub(super) local_counter: u64,
1578}
1579impl WasmBackend {
1580 pub fn new() -> Self {
1582 Self::default()
1583 }
1584 pub(super) fn fresh_local(&mut self) -> String {
1586 let n = self.local_counter;
1587 self.local_counter += 1;
1588 format!("_t{}", n)
1589 }
1590 pub(super) fn lcnf_type_to_wasm(ty: &LcnfType) -> WasmType {
1592 match ty {
1593 LcnfType::Nat => WasmType::I64,
1594 LcnfType::Erased | LcnfType::Irrelevant | LcnfType::Unit => WasmType::I32,
1595 LcnfType::LcnfString => WasmType::I32,
1596 LcnfType::Fun(_, _) => WasmType::I32,
1597 LcnfType::Ctor(_, _) => WasmType::I32,
1598 LcnfType::Object => WasmType::I32,
1599 LcnfType::Var(_) => WasmType::I32,
1600 }
1601 }
1602 pub(super) fn compile_expr(
1605 &mut self,
1606 expr: &LcnfExpr,
1607 locals: &mut Vec<(String, WasmType)>,
1608 ) -> Vec<WasmInstr> {
1609 match expr {
1610 LcnfExpr::Let {
1611 id,
1612 ty,
1613 value,
1614 body,
1615 ..
1616 } => {
1617 let local_name = format!("x{}", id.0);
1618 let val_type = Self::lcnf_type_to_wasm(ty);
1619 locals.push((local_name.clone(), val_type));
1620 let mut instrs = self.compile_let_value(value, locals);
1621 instrs.push(WasmInstr::LocalSet(local_name));
1622 instrs.extend(self.compile_expr(body, locals));
1623 instrs
1624 }
1625 LcnfExpr::Case {
1626 scrutinee,
1627 alts,
1628 default,
1629 ..
1630 } => self.compile_case(scrutinee, alts, default.as_deref(), locals),
1631 LcnfExpr::Return(arg) => self.compile_arg(arg),
1632 LcnfExpr::Unreachable => vec![WasmInstr::Unreachable],
1633 LcnfExpr::TailCall(func, args) => {
1634 let mut instrs = Vec::new();
1635 for arg in args {
1636 instrs.extend(self.compile_arg(arg));
1637 }
1638 match func {
1639 LcnfArg::Var(_) => {
1640 instrs.extend(self.compile_arg(func));
1641 instrs.push(WasmInstr::CallIndirect);
1642 }
1643 _ => {
1644 instrs.push(WasmInstr::CallIndirect);
1645 }
1646 }
1647 instrs
1648 }
1649 }
1650 }
1651 pub(super) fn compile_arg(&self, arg: &LcnfArg) -> Vec<WasmInstr> {
1653 match arg {
1654 LcnfArg::Var(id) => vec![WasmInstr::LocalGet(format!("x{}", id.0))],
1655 LcnfArg::Lit(LcnfLit::Nat(n)) => vec![WasmInstr::I64Const(*n as i64)],
1656 LcnfArg::Lit(LcnfLit::Str(_)) => vec![WasmInstr::I32Const(0)],
1657 LcnfArg::Erased => vec![WasmInstr::I32Const(0)],
1658 LcnfArg::Type(_) => vec![WasmInstr::I32Const(0)],
1659 }
1660 }
1661 pub(super) fn compile_let_value(
1663 &mut self,
1664 val: &LcnfLetValue,
1665 _locals: &mut Vec<(String, WasmType)>,
1666 ) -> Vec<WasmInstr> {
1667 match val {
1668 LcnfLetValue::Lit(lit) => match lit {
1669 LcnfLit::Nat(n) => vec![WasmInstr::I64Const(*n as i64)],
1670 LcnfLit::Str(_) => vec![WasmInstr::I32Const(0)],
1671 },
1672 LcnfLetValue::FVar(id) => vec![WasmInstr::LocalGet(format!("x{}", id.0))],
1673 LcnfLetValue::App(func, args) => {
1674 let mut instrs = Vec::new();
1675 for arg in args {
1676 instrs.extend(self.compile_arg(arg));
1677 }
1678 match func {
1679 LcnfArg::Var(_) => {
1680 instrs.extend(self.compile_arg(func));
1681 instrs.push(WasmInstr::CallIndirect);
1682 }
1683 _ => {
1684 instrs.push(WasmInstr::CallIndirect);
1685 }
1686 }
1687 instrs
1688 }
1689 LcnfLetValue::Ctor(name, _tag, args) => {
1690 let mut instrs = Vec::new();
1691 for arg in args {
1692 instrs.extend(self.compile_arg(arg));
1693 }
1694 instrs.push(WasmInstr::Call(mangle_wasm_name(name)));
1695 instrs
1696 }
1697 LcnfLetValue::Proj(_name, _idx, var) => {
1698 vec![WasmInstr::LocalGet(format!("x{}", var.0))]
1699 }
1700 LcnfLetValue::Erased => vec![WasmInstr::I32Const(0)],
1701 LcnfLetValue::Reset(var) => vec![WasmInstr::LocalGet(format!("x{}", var.0))],
1702 LcnfLetValue::Reuse(_slot, name, _tag, args) => {
1703 let mut instrs = Vec::new();
1704 for arg in args {
1705 instrs.extend(self.compile_arg(arg));
1706 }
1707 instrs.push(WasmInstr::Call(mangle_wasm_name(name)));
1708 instrs
1709 }
1710 }
1711 }
1712 pub(super) fn compile_case(
1714 &mut self,
1715 scrutinee: &LcnfVarId,
1716 alts: &[LcnfAlt],
1717 default: Option<&LcnfExpr>,
1718 locals: &mut Vec<(String, WasmType)>,
1719 ) -> Vec<WasmInstr> {
1720 let mut instrs = Vec::new();
1721 if alts.is_empty() {
1722 if let Some(def) = default {
1723 instrs.extend(self.compile_expr(def, locals));
1724 } else {
1725 instrs.push(WasmInstr::Unreachable);
1726 }
1727 return instrs;
1728 }
1729 instrs.push(WasmInstr::LocalGet(format!("x{}", scrutinee.0)));
1730 instrs.push(WasmInstr::Drop);
1731 let body_instrs = if let Some(first_alt) = alts.first() {
1732 self.compile_expr(&first_alt.body, locals)
1733 } else if let Some(def) = default {
1734 self.compile_expr(def, locals)
1735 } else {
1736 vec![WasmInstr::Unreachable]
1737 };
1738 instrs.extend(body_instrs);
1739 instrs
1740 }
1741 pub fn compile_decl(&mut self, decl: &LcnfFunDecl) -> WasmFunc {
1743 let name = mangle_wasm_name(&decl.name);
1744 let params: Vec<(String, WasmType)> = decl
1745 .params
1746 .iter()
1747 .map(|p| {
1748 let pname = format!("x{}", p.id.0);
1749 let pty = Self::lcnf_type_to_wasm(&p.ty);
1750 (pname, pty)
1751 })
1752 .collect();
1753 let results = vec![Self::lcnf_type_to_wasm(&decl.ret_type)];
1754 let mut locals: Vec<(String, WasmType)> = Vec::new();
1755 let body = self.compile_expr(&decl.body, &mut locals);
1756 WasmFunc {
1757 name,
1758 params,
1759 results,
1760 locals,
1761 body,
1762 }
1763 }
1764 pub fn emit_module(decls: &[LcnfFunDecl]) -> Result<String, String> {
1766 let mut backend = WasmBackend::new();
1767 let mut module = WasmModule::new();
1768 module.memory = Some(1);
1769 module.imports.push(WasmImport {
1770 module: "env".to_string(),
1771 name: "memory".to_string(),
1772 kind: WasmImportKind::Memory { min_pages: 1 },
1773 });
1774 module.imports.push(WasmImport {
1775 module: "oxilean".to_string(),
1776 name: "alloc".to_string(),
1777 kind: WasmImportKind::Func {
1778 params: vec![WasmType::I32],
1779 results: vec![WasmType::I32],
1780 },
1781 });
1782 for decl in decls {
1783 let func = backend.compile_decl(decl);
1784 let func_name = func.name.clone();
1785 module.functions.push(func);
1786 module.exports.push(func_name);
1787 }
1788 Ok(module.to_wat())
1789 }
1790}
1791#[allow(dead_code)]
1793#[derive(Debug)]
1794pub struct WasmX2Cache {
1795 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
1796 pub(super) cap: usize,
1797 pub(super) total_hits: u64,
1798 pub(super) total_misses: u64,
1799}
1800impl WasmX2Cache {
1801 #[allow(dead_code)]
1802 pub fn new(cap: usize) -> Self {
1803 Self {
1804 entries: Vec::new(),
1805 cap,
1806 total_hits: 0,
1807 total_misses: 0,
1808 }
1809 }
1810 #[allow(dead_code)]
1811 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
1812 for e in self.entries.iter_mut() {
1813 if e.0 == key && e.2 {
1814 e.3 += 1;
1815 self.total_hits += 1;
1816 return Some(&e.1);
1817 }
1818 }
1819 self.total_misses += 1;
1820 None
1821 }
1822 #[allow(dead_code)]
1823 pub fn put(&mut self, key: u64, data: Vec<u8>) {
1824 if self.entries.len() >= self.cap {
1825 self.entries.retain(|e| e.2);
1826 if self.entries.len() >= self.cap {
1827 self.entries.remove(0);
1828 }
1829 }
1830 self.entries.push((key, data, true, 0));
1831 }
1832 #[allow(dead_code)]
1833 pub fn invalidate(&mut self) {
1834 for e in self.entries.iter_mut() {
1835 e.2 = false;
1836 }
1837 }
1838 #[allow(dead_code)]
1839 pub fn hit_rate(&self) -> f64 {
1840 let t = self.total_hits + self.total_misses;
1841 if t == 0 {
1842 0.0
1843 } else {
1844 self.total_hits as f64 / t as f64
1845 }
1846 }
1847 #[allow(dead_code)]
1848 pub fn live_count(&self) -> usize {
1849 self.entries.iter().filter(|e| e.2).count()
1850 }
1851}
1852#[allow(dead_code)]
1854#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1855pub enum WasmExtPassPhase {
1856 Early,
1857 Middle,
1858 Late,
1859 Finalize,
1860}
1861impl WasmExtPassPhase {
1862 #[allow(dead_code)]
1863 pub fn is_early(&self) -> bool {
1864 matches!(self, Self::Early)
1865 }
1866 #[allow(dead_code)]
1867 pub fn is_middle(&self) -> bool {
1868 matches!(self, Self::Middle)
1869 }
1870 #[allow(dead_code)]
1871 pub fn is_late(&self) -> bool {
1872 matches!(self, Self::Late)
1873 }
1874 #[allow(dead_code)]
1875 pub fn is_finalize(&self) -> bool {
1876 matches!(self, Self::Finalize)
1877 }
1878 #[allow(dead_code)]
1879 pub fn order(&self) -> u32 {
1880 match self {
1881 Self::Early => 0,
1882 Self::Middle => 1,
1883 Self::Late => 2,
1884 Self::Finalize => 3,
1885 }
1886 }
1887 #[allow(dead_code)]
1888 pub fn from_order(n: u32) -> Option<Self> {
1889 match n {
1890 0 => Some(Self::Early),
1891 1 => Some(Self::Middle),
1892 2 => Some(Self::Late),
1893 3 => Some(Self::Finalize),
1894 _ => None,
1895 }
1896 }
1897}
1898#[derive(Debug, Clone, PartialEq)]
1900pub enum WasmInstr {
1901 LocalGet(String),
1903 LocalSet(String),
1905 I32Const(i32),
1907 I64Const(i64),
1909 F64Const(f64),
1911 Call(String),
1913 CallIndirect,
1915 Return,
1917 BrIf(u32),
1919 Block,
1921 Loop,
1923 End,
1925 Drop,
1927 Select,
1929 Nop,
1931 Unreachable,
1933 I32Add,
1935 I32Sub,
1937 I32Mul,
1939 I32DivS,
1941 I64Add,
1943 I64Mul,
1945 F64Add,
1947 F64Mul,
1949 F64Div,
1951 F64Sqrt,
1953 MemLoad(u32),
1955 MemStore(u32),
1957 I32Eqz,
1959 I32Eq,
1961 I32Ne,
1963 I32LtS,
1965 I32GtS,
1967 I32LeS,
1969 I32GeS,
1971 RefNull,
1973 RefIsNull,
1975 TableGet(u32),
1977 TableSet(u32),
1979}
1980#[allow(dead_code)]
1982#[derive(Debug, Clone, Default)]
1983pub struct WasmExtConstFolder {
1984 pub(super) folds: usize,
1985 pub(super) failures: usize,
1986 pub(super) enabled: bool,
1987}
1988impl WasmExtConstFolder {
1989 #[allow(dead_code)]
1990 pub fn new() -> Self {
1991 Self {
1992 folds: 0,
1993 failures: 0,
1994 enabled: true,
1995 }
1996 }
1997 #[allow(dead_code)]
1998 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1999 self.folds += 1;
2000 a.checked_add(b)
2001 }
2002 #[allow(dead_code)]
2003 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
2004 self.folds += 1;
2005 a.checked_sub(b)
2006 }
2007 #[allow(dead_code)]
2008 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
2009 self.folds += 1;
2010 a.checked_mul(b)
2011 }
2012 #[allow(dead_code)]
2013 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
2014 if b == 0 {
2015 self.failures += 1;
2016 None
2017 } else {
2018 self.folds += 1;
2019 a.checked_div(b)
2020 }
2021 }
2022 #[allow(dead_code)]
2023 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
2024 if b == 0 {
2025 self.failures += 1;
2026 None
2027 } else {
2028 self.folds += 1;
2029 a.checked_rem(b)
2030 }
2031 }
2032 #[allow(dead_code)]
2033 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
2034 self.folds += 1;
2035 a.checked_neg()
2036 }
2037 #[allow(dead_code)]
2038 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
2039 if s >= 64 {
2040 self.failures += 1;
2041 None
2042 } else {
2043 self.folds += 1;
2044 a.checked_shl(s)
2045 }
2046 }
2047 #[allow(dead_code)]
2048 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
2049 if s >= 64 {
2050 self.failures += 1;
2051 None
2052 } else {
2053 self.folds += 1;
2054 a.checked_shr(s)
2055 }
2056 }
2057 #[allow(dead_code)]
2058 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
2059 self.folds += 1;
2060 a & b
2061 }
2062 #[allow(dead_code)]
2063 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
2064 self.folds += 1;
2065 a | b
2066 }
2067 #[allow(dead_code)]
2068 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
2069 self.folds += 1;
2070 a ^ b
2071 }
2072 #[allow(dead_code)]
2073 pub fn not_i64(&mut self, a: i64) -> i64 {
2074 self.folds += 1;
2075 !a
2076 }
2077 #[allow(dead_code)]
2078 pub fn fold_count(&self) -> usize {
2079 self.folds
2080 }
2081 #[allow(dead_code)]
2082 pub fn failure_count(&self) -> usize {
2083 self.failures
2084 }
2085 #[allow(dead_code)]
2086 pub fn enable(&mut self) {
2087 self.enabled = true;
2088 }
2089 #[allow(dead_code)]
2090 pub fn disable(&mut self) {
2091 self.enabled = false;
2092 }
2093 #[allow(dead_code)]
2094 pub fn is_enabled(&self) -> bool {
2095 self.enabled
2096 }
2097}
2098#[derive(Debug, Clone, PartialEq)]
2100pub struct WasmFunc {
2101 pub name: String,
2103 pub params: Vec<(String, WasmType)>,
2105 pub results: Vec<WasmType>,
2107 pub locals: Vec<(String, WasmType)>,
2109 pub body: Vec<WasmInstr>,
2111}
2112#[allow(dead_code)]
2114#[derive(Debug, Clone, Default)]
2115pub struct WasmExtLiveness {
2116 pub live_in: Vec<Vec<usize>>,
2117 pub live_out: Vec<Vec<usize>>,
2118 pub defs: Vec<Vec<usize>>,
2119 pub uses: Vec<Vec<usize>>,
2120}
2121impl WasmExtLiveness {
2122 #[allow(dead_code)]
2123 pub fn new(n: usize) -> Self {
2124 Self {
2125 live_in: vec![Vec::new(); n],
2126 live_out: vec![Vec::new(); n],
2127 defs: vec![Vec::new(); n],
2128 uses: vec![Vec::new(); n],
2129 }
2130 }
2131 #[allow(dead_code)]
2132 pub fn live_in(&self, b: usize, v: usize) -> bool {
2133 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2134 }
2135 #[allow(dead_code)]
2136 pub fn live_out(&self, b: usize, v: usize) -> bool {
2137 self.live_out
2138 .get(b)
2139 .map(|s| s.contains(&v))
2140 .unwrap_or(false)
2141 }
2142 #[allow(dead_code)]
2143 pub fn add_def(&mut self, b: usize, v: usize) {
2144 if let Some(s) = self.defs.get_mut(b) {
2145 if !s.contains(&v) {
2146 s.push(v);
2147 }
2148 }
2149 }
2150 #[allow(dead_code)]
2151 pub fn add_use(&mut self, b: usize, v: usize) {
2152 if let Some(s) = self.uses.get_mut(b) {
2153 if !s.contains(&v) {
2154 s.push(v);
2155 }
2156 }
2157 }
2158 #[allow(dead_code)]
2159 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
2160 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2161 }
2162 #[allow(dead_code)]
2163 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
2164 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2165 }
2166}
2167#[derive(Debug, Clone, PartialEq)]
2169pub struct WasmImport {
2170 pub module: String,
2172 pub name: String,
2174 pub kind: WasmImportKind,
2176}
2177#[allow(dead_code)]
2178pub struct WasmConstantFoldingHelper;
2179impl WasmConstantFoldingHelper {
2180 #[allow(dead_code)]
2181 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
2182 a.checked_add(b)
2183 }
2184 #[allow(dead_code)]
2185 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
2186 a.checked_sub(b)
2187 }
2188 #[allow(dead_code)]
2189 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
2190 a.checked_mul(b)
2191 }
2192 #[allow(dead_code)]
2193 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
2194 if b == 0 {
2195 None
2196 } else {
2197 a.checked_div(b)
2198 }
2199 }
2200 #[allow(dead_code)]
2201 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
2202 a + b
2203 }
2204 #[allow(dead_code)]
2205 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
2206 a * b
2207 }
2208 #[allow(dead_code)]
2209 pub fn fold_neg_i64(a: i64) -> Option<i64> {
2210 a.checked_neg()
2211 }
2212 #[allow(dead_code)]
2213 pub fn fold_not_bool(a: bool) -> bool {
2214 !a
2215 }
2216 #[allow(dead_code)]
2217 pub fn fold_and_bool(a: bool, b: bool) -> bool {
2218 a && b
2219 }
2220 #[allow(dead_code)]
2221 pub fn fold_or_bool(a: bool, b: bool) -> bool {
2222 a || b
2223 }
2224 #[allow(dead_code)]
2225 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
2226 a.checked_shl(b)
2227 }
2228 #[allow(dead_code)]
2229 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
2230 a.checked_shr(b)
2231 }
2232 #[allow(dead_code)]
2233 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
2234 if b == 0 {
2235 None
2236 } else {
2237 Some(a % b)
2238 }
2239 }
2240 #[allow(dead_code)]
2241 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
2242 a & b
2243 }
2244 #[allow(dead_code)]
2245 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
2246 a | b
2247 }
2248 #[allow(dead_code)]
2249 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
2250 a ^ b
2251 }
2252 #[allow(dead_code)]
2253 pub fn fold_bitnot_i64(a: i64) -> i64 {
2254 !a
2255 }
2256}
2257#[allow(dead_code)]
2258#[derive(Debug, Clone, PartialEq)]
2259pub enum WasmPassPhase {
2260 Analysis,
2261 Transformation,
2262 Verification,
2263 Cleanup,
2264}
2265impl WasmPassPhase {
2266 #[allow(dead_code)]
2267 pub fn name(&self) -> &str {
2268 match self {
2269 WasmPassPhase::Analysis => "analysis",
2270 WasmPassPhase::Transformation => "transformation",
2271 WasmPassPhase::Verification => "verification",
2272 WasmPassPhase::Cleanup => "cleanup",
2273 }
2274 }
2275 #[allow(dead_code)]
2276 pub fn is_modifying(&self) -> bool {
2277 matches!(self, WasmPassPhase::Transformation | WasmPassPhase::Cleanup)
2278 }
2279}
2280#[allow(dead_code)]
2282#[derive(Debug, Clone, Default)]
2283pub struct WasmX2Liveness {
2284 pub live_in: Vec<Vec<usize>>,
2285 pub live_out: Vec<Vec<usize>>,
2286 pub defs: Vec<Vec<usize>>,
2287 pub uses: Vec<Vec<usize>>,
2288}
2289impl WasmX2Liveness {
2290 #[allow(dead_code)]
2291 pub fn new(n: usize) -> Self {
2292 Self {
2293 live_in: vec![Vec::new(); n],
2294 live_out: vec![Vec::new(); n],
2295 defs: vec![Vec::new(); n],
2296 uses: vec![Vec::new(); n],
2297 }
2298 }
2299 #[allow(dead_code)]
2300 pub fn live_in(&self, b: usize, v: usize) -> bool {
2301 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2302 }
2303 #[allow(dead_code)]
2304 pub fn live_out(&self, b: usize, v: usize) -> bool {
2305 self.live_out
2306 .get(b)
2307 .map(|s| s.contains(&v))
2308 .unwrap_or(false)
2309 }
2310 #[allow(dead_code)]
2311 pub fn add_def(&mut self, b: usize, v: usize) {
2312 if let Some(s) = self.defs.get_mut(b) {
2313 if !s.contains(&v) {
2314 s.push(v);
2315 }
2316 }
2317 }
2318 #[allow(dead_code)]
2319 pub fn add_use(&mut self, b: usize, v: usize) {
2320 if let Some(s) = self.uses.get_mut(b) {
2321 if !s.contains(&v) {
2322 s.push(v);
2323 }
2324 }
2325 }
2326 #[allow(dead_code)]
2327 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
2328 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2329 }
2330 #[allow(dead_code)]
2331 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
2332 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2333 }
2334}