1use crate::lcnf::*;
6use std::collections::{HashMap, HashSet};
7
8use super::functions::*;
9use std::collections::VecDeque;
10
11#[derive(Debug, Clone, Default)]
13pub struct SpecializationStats {
14 pub type_specializations: usize,
16 pub const_specializations: usize,
18 pub closure_specializations: usize,
20 pub call_sites_redirected: usize,
22 pub total_code_growth: usize,
24 pub skipped_budget: usize,
26 pub skipped_limit: usize,
28 pub functions_analyzed: usize,
30}
31#[allow(dead_code)]
32pub struct SpecPassRegistry {
33 pub(super) configs: Vec<SpecPassConfig>,
34 pub(super) stats: std::collections::HashMap<String, SpecPassStats>,
35}
36impl SpecPassRegistry {
37 #[allow(dead_code)]
38 pub fn new() -> Self {
39 SpecPassRegistry {
40 configs: Vec::new(),
41 stats: std::collections::HashMap::new(),
42 }
43 }
44 #[allow(dead_code)]
45 pub fn register(&mut self, config: SpecPassConfig) {
46 self.stats
47 .insert(config.pass_name.clone(), SpecPassStats::new());
48 self.configs.push(config);
49 }
50 #[allow(dead_code)]
51 pub fn enabled_passes(&self) -> Vec<&SpecPassConfig> {
52 self.configs.iter().filter(|c| c.enabled).collect()
53 }
54 #[allow(dead_code)]
55 pub fn get_stats(&self, name: &str) -> Option<&SpecPassStats> {
56 self.stats.get(name)
57 }
58 #[allow(dead_code)]
59 pub fn total_passes(&self) -> usize {
60 self.configs.len()
61 }
62 #[allow(dead_code)]
63 pub fn enabled_count(&self) -> usize {
64 self.enabled_passes().len()
65 }
66 #[allow(dead_code)]
67 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
68 if let Some(stats) = self.stats.get_mut(name) {
69 stats.record_run(changes, time_ms, iter);
70 }
71 }
72}
73#[derive(Debug, Clone)]
75pub struct SpecializationConfig {
76 pub max_specializations: usize,
78 pub specialize_closures: bool,
80 pub specialize_numerics: bool,
82 pub size_threshold: usize,
84 pub growth_factor: f64,
86 pub allow_recursive: bool,
88 pub specialize_type_params: bool,
90 pub max_recursive_depth: usize,
92}
93#[derive(Debug, Clone)]
95pub struct SpecializedDecl {
96 pub key: SpecializationKey,
98 pub decl: LcnfFunDecl,
100 pub code_growth: usize,
102 pub from_recursive: bool,
104}
105#[allow(dead_code)]
106#[derive(Debug, Clone)]
107pub struct SpecCacheEntry {
108 pub key: String,
109 pub data: Vec<u8>,
110 pub timestamp: u64,
111 pub valid: bool,
112}
113#[derive(Debug, Clone, PartialEq, Eq, Hash)]
115pub struct SpecClosureArg {
116 pub known_fn: Option<String>,
118 pub param_idx: usize,
120}
121pub struct SpecializationPass {
123 pub(super) config: SpecializationConfig,
124 pub(super) cache: SpecializationCache,
125 pub(super) stats: SpecializationStats,
126 pub(super) next_id: u64,
127 pub(super) var_to_fn: HashMap<LcnfVarId, String>,
129 pub(super) original_sizes: HashMap<String, usize>,
131}
132impl SpecializationPass {
133 pub fn new(config: SpecializationConfig) -> Self {
135 SpecializationPass {
136 config,
137 cache: SpecializationCache::new(),
138 stats: SpecializationStats::default(),
139 next_id: 5000,
140 var_to_fn: HashMap::new(),
141 original_sizes: HashMap::new(),
142 }
143 }
144 pub fn stats(&self) -> &SpecializationStats {
146 &self.stats
147 }
148 pub(super) fn fresh_id(&mut self) -> LcnfVarId {
150 let id = self.next_id;
151 self.next_id += 1;
152 LcnfVarId(id)
153 }
154 pub(super) fn run(&mut self, module: &mut LcnfModule) {
156 for decl in &module.fun_decls {
157 let size = count_instructions(&decl.body);
158 self.original_sizes.insert(decl.name.clone(), size);
159 }
160 let decl_names: HashSet<String> = module.fun_decls.iter().map(|d| d.name.clone()).collect();
161 let mut all_sites: Vec<(String, Vec<SpecCallSite>)> = Vec::new();
162 for decl in &module.fun_decls {
163 self.stats.functions_analyzed += 1;
164 let known_consts: HashMap<LcnfVarId, LcnfLit> = HashMap::new();
165 let sites =
166 find_specialization_sites(&decl.body, &known_consts, &self.var_to_fn, &decl_names);
167 if !sites.is_empty() {
168 all_sites.push((decl.name.clone(), sites));
169 }
170 }
171 let mut new_decls: Vec<LcnfFunDecl> = Vec::new();
172 let total_original_size: usize = self.original_sizes.values().sum();
173 let budget = (total_original_size as f64 * self.config.growth_factor) as usize;
174 for (_caller, sites) in &all_sites {
175 for site in sites {
176 if self.cache.total_growth >= budget {
177 self.stats.skipped_budget += 1;
178 continue;
179 }
180 if self.cache.specialization_count(&site.callee) >= self.config.max_specializations
181 {
182 self.stats.skipped_limit += 1;
183 continue;
184 }
185 let key = self.build_key(site);
186 if key.is_trivial() {
187 continue;
188 }
189 if self.cache.lookup(&key).is_some() {
190 continue;
191 }
192 if let Some(original) = module.fun_decls.iter().find(|d| d.name == site.callee) {
193 let original_size = count_instructions(&original.body);
194 if original_size > self.config.size_threshold {
195 self.stats.skipped_budget += 1;
196 continue;
197 }
198 if let Some(spec_decl) = self.create_specialization(original, &key) {
199 let growth = count_instructions(&spec_decl.decl.body);
200 self.cache
201 .insert(key.clone(), spec_decl.decl.name.clone(), growth);
202 self.update_stats(&key);
203 new_decls.push(spec_decl.decl);
204 }
205 }
206 }
207 }
208 module.fun_decls.extend(new_decls);
209 self.redirect_call_sites(module);
210 }
211 pub(super) fn build_key(&self, site: &SpecCallSite) -> SpecializationKey {
213 let type_args = if site.type_args.is_empty() {
214 vec![]
215 } else {
216 site.type_args.clone()
217 };
218 SpecializationKey {
219 original: site.callee.clone(),
220 type_args,
221 const_args: site.const_args.clone(),
222 closure_args: site.closure_args.clone(),
223 }
224 }
225 pub(super) fn create_specialization(
227 &mut self,
228 original: &LcnfFunDecl,
229 key: &SpecializationKey,
230 ) -> Option<SpecializedDecl> {
231 if !self.config.allow_recursive && original.is_recursive {
232 return None;
233 }
234 let spec_name = key.mangled_name();
235 let mut spec_body = original.body.clone();
236 let mut spec_params = original.params.clone();
237 for (i, ca) in key.const_args.iter().enumerate() {
238 match ca {
239 SpecConstArg::Nat(n) => {
240 if i < spec_params.len() {
241 let param_id = spec_params[i].id;
242 self.substitute_constant(&mut spec_body, param_id, &LcnfLit::Nat(*n));
243 spec_params[i].erased = true;
244 }
245 }
246 SpecConstArg::Str(s) => {
247 if i < spec_params.len() {
248 let param_id = spec_params[i].id;
249 self.substitute_constant(
250 &mut spec_body,
251 param_id,
252 &LcnfLit::Str(s.clone()),
253 );
254 spec_params[i].erased = true;
255 }
256 }
257 SpecConstArg::Unknown => {}
258 }
259 }
260 for (i, ta) in key.type_args.iter().enumerate() {
261 if let SpecTypeArg::Concrete(ty) = ta {
262 if i < spec_params.len() {
263 spec_params[i].ty = ty.clone();
264 }
265 }
266 }
267 let active_params: Vec<LcnfParam> = spec_params.into_iter().filter(|p| !p.erased).collect();
268 let spec_decl = LcnfFunDecl {
269 name: spec_name.clone(),
270 original_name: original.original_name.clone(),
271 params: active_params,
272 ret_type: original.ret_type.clone(),
273 body: spec_body,
274 is_recursive: original.is_recursive,
275 is_lifted: true,
276 inline_cost: original.inline_cost,
277 };
278 let code_growth = count_instructions(&spec_decl.body);
279 Some(SpecializedDecl {
280 key: key.clone(),
281 decl: spec_decl,
282 code_growth,
283 from_recursive: original.is_recursive,
284 })
285 }
286 pub(super) fn substitute_constant(&self, expr: &mut LcnfExpr, var: LcnfVarId, lit: &LcnfLit) {
288 match expr {
289 LcnfExpr::Let {
290 id, value, body, ..
291 } => {
292 self.substitute_constant_in_value(value, var, lit);
293 if *id != var {
294 self.substitute_constant(body, var, lit);
295 }
296 }
297 LcnfExpr::Case {
298 scrutinee,
299 alts,
300 default,
301 ..
302 } => {
303 if *scrutinee == var {}
304 for alt in alts.iter_mut() {
305 let shadows = alt.params.iter().any(|p| p.id == var);
306 if !shadows {
307 self.substitute_constant(&mut alt.body, var, lit);
308 }
309 }
310 if let Some(def) = default {
311 self.substitute_constant(def, var, lit);
312 }
313 }
314 LcnfExpr::Return(arg) => {
315 self.substitute_arg(arg, var, lit);
316 }
317 LcnfExpr::TailCall(func, args) => {
318 self.substitute_arg(func, var, lit);
319 for a in args.iter_mut() {
320 self.substitute_arg(a, var, lit);
321 }
322 }
323 LcnfExpr::Unreachable => {}
324 }
325 }
326 pub(super) fn substitute_constant_in_value(
328 &self,
329 value: &mut LcnfLetValue,
330 var: LcnfVarId,
331 lit: &LcnfLit,
332 ) {
333 match value {
334 LcnfLetValue::App(func, args) => {
335 self.substitute_arg(func, var, lit);
336 for a in args.iter_mut() {
337 self.substitute_arg(a, var, lit);
338 }
339 }
340 LcnfLetValue::Proj(_, _, obj) => if *obj == var {},
341 LcnfLetValue::Ctor(_, _, args) => {
342 for a in args.iter_mut() {
343 self.substitute_arg(a, var, lit);
344 }
345 }
346 LcnfLetValue::FVar(v) => {
347 if *v == var {
348 *value = LcnfLetValue::Lit(lit.clone());
349 }
350 }
351 LcnfLetValue::Lit(_)
352 | LcnfLetValue::Erased
353 | LcnfLetValue::Reset(_)
354 | LcnfLetValue::Reuse(_, _, _, _) => {}
355 }
356 }
357 pub(super) fn substitute_arg(&self, arg: &mut LcnfArg, var: LcnfVarId, lit: &LcnfLit) {
359 if let LcnfArg::Var(v) = arg {
360 if *v == var {
361 *arg = LcnfArg::Lit(lit.clone());
362 }
363 }
364 }
365 pub(super) fn update_stats(&mut self, key: &SpecializationKey) {
367 let has_type = key
368 .type_args
369 .iter()
370 .any(|a| matches!(a, SpecTypeArg::Concrete(_)));
371 let has_const = key
372 .const_args
373 .iter()
374 .any(|a| !matches!(a, SpecConstArg::Unknown));
375 let has_closure = key.closure_args.iter().any(|a| a.known_fn.is_some());
376 if has_type {
377 self.stats.type_specializations += 1;
378 }
379 if has_const {
380 self.stats.const_specializations += 1;
381 }
382 if has_closure {
383 self.stats.closure_specializations += 1;
384 }
385 }
386 pub(super) fn redirect_call_sites(&mut self, module: &mut LcnfModule) {
388 let redirects: HashMap<SpecializationKey, String> = self.cache.entries.clone();
389 if redirects.is_empty() {
390 return;
391 }
392 for decl in &mut module.fun_decls {
393 let redirected = self.redirect_in_expr(&mut decl.body, &redirects);
394 self.stats.call_sites_redirected += redirected;
395 }
396 }
397 pub(super) fn redirect_in_expr(
399 &self,
400 expr: &mut LcnfExpr,
401 redirects: &HashMap<SpecializationKey, String>,
402 ) -> usize {
403 let mut count = 0;
404 let mut local_consts: HashMap<LcnfVarId, LcnfLit> = HashMap::new();
405 let mut local_fn_map: HashMap<LcnfVarId, String> = self.var_to_fn.clone();
406 self.redirect_inner(
407 expr,
408 redirects,
409 &mut local_consts,
410 &mut local_fn_map,
411 &mut count,
412 );
413 count
414 }
415 pub(super) fn redirect_inner(
417 &self,
418 expr: &mut LcnfExpr,
419 redirects: &HashMap<SpecializationKey, String>,
420 local_consts: &mut HashMap<LcnfVarId, LcnfLit>,
421 local_fn_map: &mut HashMap<LcnfVarId, String>,
422 count: &mut usize,
423 ) {
424 match expr {
425 LcnfExpr::Let {
426 id, value, body, ..
427 } => {
428 if let LcnfLetValue::App(func, args) = value {
429 if let LcnfArg::Var(v) = func {
430 if let Some(fn_name) = local_fn_map.get(v).cloned() {
431 let const_args = Self::build_const_args(args, local_consts);
432 let closure_args = Self::build_closure_args(args, local_fn_map);
433 for (key, spec_name) in redirects {
434 if key.original == fn_name
435 && key.const_args == const_args
436 && (key.closure_args.is_empty()
437 || key.closure_args == closure_args)
438 {
439 if let Some(spec_var) = local_fn_map
440 .iter()
441 .find(|(_, name)| *name == spec_name)
442 .map(|(var, _)| *var)
443 {
444 *func = LcnfArg::Var(spec_var);
445 *count += 1;
446 }
447 break;
448 }
449 }
450 }
451 }
452 }
453 if let LcnfLetValue::Lit(lit) = value {
454 local_consts.insert(*id, lit.clone());
455 }
456 if let LcnfLetValue::FVar(v) = value {
457 if let Some(fn_name) = local_fn_map.get(v).cloned() {
458 local_fn_map.insert(*id, fn_name);
459 }
460 }
461 self.redirect_inner(body, redirects, local_consts, local_fn_map, count);
462 }
463 LcnfExpr::Case { alts, default, .. } => {
464 for alt in alts.iter_mut() {
465 let mut alt_consts = local_consts.clone();
466 let mut alt_fn_map = local_fn_map.clone();
467 self.redirect_inner(
468 &mut alt.body,
469 redirects,
470 &mut alt_consts,
471 &mut alt_fn_map,
472 count,
473 );
474 }
475 if let Some(def) = default {
476 let mut def_consts = local_consts.clone();
477 let mut def_fn_map = local_fn_map.clone();
478 self.redirect_inner(def, redirects, &mut def_consts, &mut def_fn_map, count);
479 }
480 }
481 LcnfExpr::TailCall(func, args) => {
482 if let LcnfArg::Var(v) = func {
483 if let Some(fn_name) = local_fn_map.get(v).cloned() {
484 let const_args = Self::build_const_args(args, local_consts);
485 let closure_args = Self::build_closure_args(args, local_fn_map);
486 for (key, spec_name) in redirects {
487 if key.original == fn_name
488 && key.const_args == const_args
489 && (key.closure_args.is_empty() || key.closure_args == closure_args)
490 {
491 if let Some(spec_var) = local_fn_map
492 .iter()
493 .find(|(_, name)| *name == spec_name)
494 .map(|(var, _)| *var)
495 {
496 *func = LcnfArg::Var(spec_var);
497 *count += 1;
498 }
499 break;
500 }
501 }
502 }
503 }
504 }
505 LcnfExpr::Return(_) | LcnfExpr::Unreachable => {}
506 }
507 }
508 pub(super) fn build_const_args(
510 args: &[LcnfArg],
511 local_consts: &HashMap<LcnfVarId, LcnfLit>,
512 ) -> Vec<SpecConstArg> {
513 args.iter()
514 .map(|arg| match arg {
515 LcnfArg::Lit(LcnfLit::Nat(n)) => SpecConstArg::Nat(*n),
516 LcnfArg::Lit(LcnfLit::Str(s)) => SpecConstArg::Str(s.clone()),
517 LcnfArg::Var(v) => match local_consts.get(v) {
518 Some(LcnfLit::Nat(n)) => SpecConstArg::Nat(*n),
519 Some(LcnfLit::Int(_)) => SpecConstArg::Unknown,
520 Some(LcnfLit::Str(s)) => SpecConstArg::Str(s.clone()),
521 None => SpecConstArg::Unknown,
522 },
523 _ => SpecConstArg::Unknown,
524 })
525 .collect()
526 }
527 pub(super) fn build_closure_args(
529 args: &[LcnfArg],
530 local_fn_map: &HashMap<LcnfVarId, String>,
531 ) -> Vec<SpecClosureArg> {
532 args.iter()
533 .enumerate()
534 .map(|(i, arg)| SpecClosureArg {
535 known_fn: match arg {
536 LcnfArg::Var(v) => local_fn_map.get(v).cloned(),
537 _ => None,
538 },
539 param_idx: i,
540 })
541 .collect()
542 }
543}
544#[allow(dead_code)]
546#[derive(Debug, Default)]
547pub struct SpecExtPassRegistry {
548 pub(super) configs: Vec<SpecExtPassConfig>,
549 pub(super) stats: Vec<SpecExtPassStats>,
550}
551impl SpecExtPassRegistry {
552 #[allow(dead_code)]
553 pub fn new() -> Self {
554 Self::default()
555 }
556 #[allow(dead_code)]
557 pub fn register(&mut self, c: SpecExtPassConfig) {
558 self.stats.push(SpecExtPassStats::new());
559 self.configs.push(c);
560 }
561 #[allow(dead_code)]
562 pub fn len(&self) -> usize {
563 self.configs.len()
564 }
565 #[allow(dead_code)]
566 pub fn is_empty(&self) -> bool {
567 self.configs.is_empty()
568 }
569 #[allow(dead_code)]
570 pub fn get(&self, i: usize) -> Option<&SpecExtPassConfig> {
571 self.configs.get(i)
572 }
573 #[allow(dead_code)]
574 pub fn get_stats(&self, i: usize) -> Option<&SpecExtPassStats> {
575 self.stats.get(i)
576 }
577 #[allow(dead_code)]
578 pub fn enabled_passes(&self) -> Vec<&SpecExtPassConfig> {
579 self.configs.iter().filter(|c| c.enabled).collect()
580 }
581 #[allow(dead_code)]
582 pub fn passes_in_phase(&self, ph: &SpecExtPassPhase) -> Vec<&SpecExtPassConfig> {
583 self.configs
584 .iter()
585 .filter(|c| c.enabled && &c.phase == ph)
586 .collect()
587 }
588 #[allow(dead_code)]
589 pub fn total_nodes_visited(&self) -> usize {
590 self.stats.iter().map(|s| s.nodes_visited).sum()
591 }
592 #[allow(dead_code)]
593 pub fn any_changed(&self) -> bool {
594 self.stats.iter().any(|s| s.changed)
595 }
596}
597#[allow(dead_code)]
599#[derive(Debug)]
600pub struct SpecExtCache {
601 pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
602 pub(super) cap: usize,
603 pub(super) total_hits: u64,
604 pub(super) total_misses: u64,
605}
606impl SpecExtCache {
607 #[allow(dead_code)]
608 pub fn new(cap: usize) -> Self {
609 Self {
610 entries: Vec::new(),
611 cap,
612 total_hits: 0,
613 total_misses: 0,
614 }
615 }
616 #[allow(dead_code)]
617 pub fn get(&mut self, key: u64) -> Option<&[u8]> {
618 for e in self.entries.iter_mut() {
619 if e.0 == key && e.2 {
620 e.3 += 1;
621 self.total_hits += 1;
622 return Some(&e.1);
623 }
624 }
625 self.total_misses += 1;
626 None
627 }
628 #[allow(dead_code)]
629 pub fn put(&mut self, key: u64, data: Vec<u8>) {
630 if self.entries.len() >= self.cap {
631 self.entries.retain(|e| e.2);
632 if self.entries.len() >= self.cap {
633 self.entries.remove(0);
634 }
635 }
636 self.entries.push((key, data, true, 0));
637 }
638 #[allow(dead_code)]
639 pub fn invalidate(&mut self) {
640 for e in self.entries.iter_mut() {
641 e.2 = false;
642 }
643 }
644 #[allow(dead_code)]
645 pub fn hit_rate(&self) -> f64 {
646 let t = self.total_hits + self.total_misses;
647 if t == 0 {
648 0.0
649 } else {
650 self.total_hits as f64 / t as f64
651 }
652 }
653 #[allow(dead_code)]
654 pub fn live_count(&self) -> usize {
655 self.entries.iter().filter(|e| e.2).count()
656 }
657}
658#[allow(dead_code)]
660#[derive(Debug, Clone)]
661pub struct SpecExtDepGraph {
662 pub(super) n: usize,
663 pub(super) adj: Vec<Vec<usize>>,
664 pub(super) rev: Vec<Vec<usize>>,
665 pub(super) edge_count: usize,
666}
667impl SpecExtDepGraph {
668 #[allow(dead_code)]
669 pub fn new(n: usize) -> Self {
670 Self {
671 n,
672 adj: vec![Vec::new(); n],
673 rev: vec![Vec::new(); n],
674 edge_count: 0,
675 }
676 }
677 #[allow(dead_code)]
678 pub fn add_edge(&mut self, from: usize, to: usize) {
679 if from < self.n && to < self.n {
680 if !self.adj[from].contains(&to) {
681 self.adj[from].push(to);
682 self.rev[to].push(from);
683 self.edge_count += 1;
684 }
685 }
686 }
687 #[allow(dead_code)]
688 pub fn succs(&self, n: usize) -> &[usize] {
689 self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
690 }
691 #[allow(dead_code)]
692 pub fn preds(&self, n: usize) -> &[usize] {
693 self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
694 }
695 #[allow(dead_code)]
696 pub fn topo_sort(&self) -> Option<Vec<usize>> {
697 let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
698 let mut q: std::collections::VecDeque<usize> =
699 (0..self.n).filter(|&i| deg[i] == 0).collect();
700 let mut out = Vec::with_capacity(self.n);
701 while let Some(u) = q.pop_front() {
702 out.push(u);
703 for &v in &self.adj[u] {
704 deg[v] -= 1;
705 if deg[v] == 0 {
706 q.push_back(v);
707 }
708 }
709 }
710 if out.len() == self.n {
711 Some(out)
712 } else {
713 None
714 }
715 }
716 #[allow(dead_code)]
717 pub fn has_cycle(&self) -> bool {
718 self.topo_sort().is_none()
719 }
720 #[allow(dead_code)]
721 pub fn reachable(&self, start: usize) -> Vec<usize> {
722 let mut vis = vec![false; self.n];
723 let mut stk = vec![start];
724 let mut out = Vec::new();
725 while let Some(u) = stk.pop() {
726 if u < self.n && !vis[u] {
727 vis[u] = true;
728 out.push(u);
729 for &v in &self.adj[u] {
730 if !vis[v] {
731 stk.push(v);
732 }
733 }
734 }
735 }
736 out
737 }
738 #[allow(dead_code)]
739 pub fn scc(&self) -> Vec<Vec<usize>> {
740 let mut visited = vec![false; self.n];
741 let mut order = Vec::new();
742 for i in 0..self.n {
743 if !visited[i] {
744 let mut stk = vec![(i, 0usize)];
745 while let Some((u, idx)) = stk.last_mut() {
746 if !visited[*u] {
747 visited[*u] = true;
748 }
749 if *idx < self.adj[*u].len() {
750 let v = self.adj[*u][*idx];
751 *idx += 1;
752 if !visited[v] {
753 stk.push((v, 0));
754 }
755 } else {
756 order.push(*u);
757 stk.pop();
758 }
759 }
760 }
761 }
762 let mut comp = vec![usize::MAX; self.n];
763 let mut components: Vec<Vec<usize>> = Vec::new();
764 for &start in order.iter().rev() {
765 if comp[start] == usize::MAX {
766 let cid = components.len();
767 let mut component = Vec::new();
768 let mut stk = vec![start];
769 while let Some(u) = stk.pop() {
770 if comp[u] == usize::MAX {
771 comp[u] = cid;
772 component.push(u);
773 for &v in &self.rev[u] {
774 if comp[v] == usize::MAX {
775 stk.push(v);
776 }
777 }
778 }
779 }
780 components.push(component);
781 }
782 }
783 components
784 }
785 #[allow(dead_code)]
786 pub fn node_count(&self) -> usize {
787 self.n
788 }
789 #[allow(dead_code)]
790 pub fn edge_count(&self) -> usize {
791 self.edge_count
792 }
793}
794#[allow(dead_code)]
796#[derive(Debug, Clone)]
797pub struct SpecExtDomTree {
798 pub(super) idom: Vec<Option<usize>>,
799 pub(super) children: Vec<Vec<usize>>,
800 pub(super) depth: Vec<usize>,
801}
802impl SpecExtDomTree {
803 #[allow(dead_code)]
804 pub fn new(n: usize) -> Self {
805 Self {
806 idom: vec![None; n],
807 children: vec![Vec::new(); n],
808 depth: vec![0; n],
809 }
810 }
811 #[allow(dead_code)]
812 pub fn set_idom(&mut self, node: usize, dom: usize) {
813 if node < self.idom.len() {
814 self.idom[node] = Some(dom);
815 if dom < self.children.len() {
816 self.children[dom].push(node);
817 }
818 self.depth[node] = if dom < self.depth.len() {
819 self.depth[dom] + 1
820 } else {
821 1
822 };
823 }
824 }
825 #[allow(dead_code)]
826 pub fn dominates(&self, a: usize, mut b: usize) -> bool {
827 if a == b {
828 return true;
829 }
830 let n = self.idom.len();
831 for _ in 0..n {
832 match self.idom.get(b).copied().flatten() {
833 None => return false,
834 Some(p) if p == a => return true,
835 Some(p) if p == b => return false,
836 Some(p) => b = p,
837 }
838 }
839 false
840 }
841 #[allow(dead_code)]
842 pub fn children_of(&self, n: usize) -> &[usize] {
843 self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
844 }
845 #[allow(dead_code)]
846 pub fn depth_of(&self, n: usize) -> usize {
847 self.depth.get(n).copied().unwrap_or(0)
848 }
849 #[allow(dead_code)]
850 pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
851 let n = self.idom.len();
852 for _ in 0..(2 * n) {
853 if a == b {
854 return a;
855 }
856 if self.depth_of(a) > self.depth_of(b) {
857 a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
858 } else {
859 b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
860 }
861 }
862 0
863 }
864}
865#[allow(dead_code)]
866#[derive(Debug, Clone, PartialEq)]
867pub enum SpecPassPhase {
868 Analysis,
869 Transformation,
870 Verification,
871 Cleanup,
872}
873impl SpecPassPhase {
874 #[allow(dead_code)]
875 pub fn name(&self) -> &str {
876 match self {
877 SpecPassPhase::Analysis => "analysis",
878 SpecPassPhase::Transformation => "transformation",
879 SpecPassPhase::Verification => "verification",
880 SpecPassPhase::Cleanup => "cleanup",
881 }
882 }
883 #[allow(dead_code)]
884 pub fn is_modifying(&self) -> bool {
885 matches!(self, SpecPassPhase::Transformation | SpecPassPhase::Cleanup)
886 }
887}
888#[allow(dead_code)]
890#[derive(Debug, Clone, PartialEq, Eq, Hash)]
891pub enum SpecExtPassPhase {
892 Early,
893 Middle,
894 Late,
895 Finalize,
896}
897impl SpecExtPassPhase {
898 #[allow(dead_code)]
899 pub fn is_early(&self) -> bool {
900 matches!(self, Self::Early)
901 }
902 #[allow(dead_code)]
903 pub fn is_middle(&self) -> bool {
904 matches!(self, Self::Middle)
905 }
906 #[allow(dead_code)]
907 pub fn is_late(&self) -> bool {
908 matches!(self, Self::Late)
909 }
910 #[allow(dead_code)]
911 pub fn is_finalize(&self) -> bool {
912 matches!(self, Self::Finalize)
913 }
914 #[allow(dead_code)]
915 pub fn order(&self) -> u32 {
916 match self {
917 Self::Early => 0,
918 Self::Middle => 1,
919 Self::Late => 2,
920 Self::Finalize => 3,
921 }
922 }
923 #[allow(dead_code)]
924 pub fn from_order(n: u32) -> Option<Self> {
925 match n {
926 0 => Some(Self::Early),
927 1 => Some(Self::Middle),
928 2 => Some(Self::Late),
929 3 => Some(Self::Finalize),
930 _ => None,
931 }
932 }
933}
934#[allow(dead_code)]
936#[derive(Debug, Clone, Default)]
937pub struct SpecExtConstFolder {
938 pub(super) folds: usize,
939 pub(super) failures: usize,
940 pub(super) enabled: bool,
941}
942impl SpecExtConstFolder {
943 #[allow(dead_code)]
944 pub fn new() -> Self {
945 Self {
946 folds: 0,
947 failures: 0,
948 enabled: true,
949 }
950 }
951 #[allow(dead_code)]
952 pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
953 self.folds += 1;
954 a.checked_add(b)
955 }
956 #[allow(dead_code)]
957 pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
958 self.folds += 1;
959 a.checked_sub(b)
960 }
961 #[allow(dead_code)]
962 pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
963 self.folds += 1;
964 a.checked_mul(b)
965 }
966 #[allow(dead_code)]
967 pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
968 if b == 0 {
969 self.failures += 1;
970 None
971 } else {
972 self.folds += 1;
973 a.checked_div(b)
974 }
975 }
976 #[allow(dead_code)]
977 pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
978 if b == 0 {
979 self.failures += 1;
980 None
981 } else {
982 self.folds += 1;
983 a.checked_rem(b)
984 }
985 }
986 #[allow(dead_code)]
987 pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
988 self.folds += 1;
989 a.checked_neg()
990 }
991 #[allow(dead_code)]
992 pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
993 if s >= 64 {
994 self.failures += 1;
995 None
996 } else {
997 self.folds += 1;
998 a.checked_shl(s)
999 }
1000 }
1001 #[allow(dead_code)]
1002 pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1003 if s >= 64 {
1004 self.failures += 1;
1005 None
1006 } else {
1007 self.folds += 1;
1008 a.checked_shr(s)
1009 }
1010 }
1011 #[allow(dead_code)]
1012 pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
1013 self.folds += 1;
1014 a & b
1015 }
1016 #[allow(dead_code)]
1017 pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1018 self.folds += 1;
1019 a | b
1020 }
1021 #[allow(dead_code)]
1022 pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1023 self.folds += 1;
1024 a ^ b
1025 }
1026 #[allow(dead_code)]
1027 pub fn not_i64(&mut self, a: i64) -> i64 {
1028 self.folds += 1;
1029 !a
1030 }
1031 #[allow(dead_code)]
1032 pub fn fold_count(&self) -> usize {
1033 self.folds
1034 }
1035 #[allow(dead_code)]
1036 pub fn failure_count(&self) -> usize {
1037 self.failures
1038 }
1039 #[allow(dead_code)]
1040 pub fn enable(&mut self) {
1041 self.enabled = true;
1042 }
1043 #[allow(dead_code)]
1044 pub fn disable(&mut self) {
1045 self.enabled = false;
1046 }
1047 #[allow(dead_code)]
1048 pub fn is_enabled(&self) -> bool {
1049 self.enabled
1050 }
1051}
1052#[allow(dead_code)]
1053#[derive(Debug, Clone, Default)]
1054pub struct SpecPassStats {
1055 pub total_runs: u32,
1056 pub successful_runs: u32,
1057 pub total_changes: u64,
1058 pub time_ms: u64,
1059 pub iterations_used: u32,
1060}
1061impl SpecPassStats {
1062 #[allow(dead_code)]
1063 pub fn new() -> Self {
1064 Self::default()
1065 }
1066 #[allow(dead_code)]
1067 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1068 self.total_runs += 1;
1069 self.successful_runs += 1;
1070 self.total_changes += changes;
1071 self.time_ms += time_ms;
1072 self.iterations_used = iterations;
1073 }
1074 #[allow(dead_code)]
1075 pub fn average_changes_per_run(&self) -> f64 {
1076 if self.total_runs == 0 {
1077 return 0.0;
1078 }
1079 self.total_changes as f64 / self.total_runs as f64
1080 }
1081 #[allow(dead_code)]
1082 pub fn success_rate(&self) -> f64 {
1083 if self.total_runs == 0 {
1084 return 0.0;
1085 }
1086 self.successful_runs as f64 / self.total_runs as f64
1087 }
1088 #[allow(dead_code)]
1089 pub fn format_summary(&self) -> String {
1090 format!(
1091 "Runs: {}/{}, Changes: {}, Time: {}ms",
1092 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1093 )
1094 }
1095}
1096#[allow(dead_code)]
1097pub struct SpecConstantFoldingHelper;
1098impl SpecConstantFoldingHelper {
1099 #[allow(dead_code)]
1100 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1101 a.checked_add(b)
1102 }
1103 #[allow(dead_code)]
1104 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1105 a.checked_sub(b)
1106 }
1107 #[allow(dead_code)]
1108 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1109 a.checked_mul(b)
1110 }
1111 #[allow(dead_code)]
1112 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1113 if b == 0 {
1114 None
1115 } else {
1116 a.checked_div(b)
1117 }
1118 }
1119 #[allow(dead_code)]
1120 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1121 a + b
1122 }
1123 #[allow(dead_code)]
1124 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1125 a * b
1126 }
1127 #[allow(dead_code)]
1128 pub fn fold_neg_i64(a: i64) -> Option<i64> {
1129 a.checked_neg()
1130 }
1131 #[allow(dead_code)]
1132 pub fn fold_not_bool(a: bool) -> bool {
1133 !a
1134 }
1135 #[allow(dead_code)]
1136 pub fn fold_and_bool(a: bool, b: bool) -> bool {
1137 a && b
1138 }
1139 #[allow(dead_code)]
1140 pub fn fold_or_bool(a: bool, b: bool) -> bool {
1141 a || b
1142 }
1143 #[allow(dead_code)]
1144 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1145 a.checked_shl(b)
1146 }
1147 #[allow(dead_code)]
1148 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1149 a.checked_shr(b)
1150 }
1151 #[allow(dead_code)]
1152 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1153 if b == 0 {
1154 None
1155 } else {
1156 Some(a % b)
1157 }
1158 }
1159 #[allow(dead_code)]
1160 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1161 a & b
1162 }
1163 #[allow(dead_code)]
1164 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1165 a | b
1166 }
1167 #[allow(dead_code)]
1168 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1169 a ^ b
1170 }
1171 #[allow(dead_code)]
1172 pub fn fold_bitnot_i64(a: i64) -> i64 {
1173 !a
1174 }
1175}
1176#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1178pub enum SpecConstArg {
1179 Nat(u64),
1181 Str(String),
1183 Unknown,
1185}
1186#[allow(dead_code)]
1187#[derive(Debug, Clone)]
1188pub struct SpecDominatorTree {
1189 pub idom: Vec<Option<u32>>,
1190 pub dom_children: Vec<Vec<u32>>,
1191 pub dom_depth: Vec<u32>,
1192}
1193impl SpecDominatorTree {
1194 #[allow(dead_code)]
1195 pub fn new(size: usize) -> Self {
1196 SpecDominatorTree {
1197 idom: vec![None; size],
1198 dom_children: vec![Vec::new(); size],
1199 dom_depth: vec![0; size],
1200 }
1201 }
1202 #[allow(dead_code)]
1203 pub fn set_idom(&mut self, node: usize, idom: u32) {
1204 self.idom[node] = Some(idom);
1205 }
1206 #[allow(dead_code)]
1207 pub fn dominates(&self, a: usize, b: usize) -> bool {
1208 if a == b {
1209 return true;
1210 }
1211 let mut cur = b;
1212 loop {
1213 match self.idom[cur] {
1214 Some(parent) if parent as usize == a => return true,
1215 Some(parent) if parent as usize == cur => return false,
1216 Some(parent) => cur = parent as usize,
1217 None => return false,
1218 }
1219 }
1220 }
1221 #[allow(dead_code)]
1222 pub fn depth(&self, node: usize) -> u32 {
1223 self.dom_depth.get(node).copied().unwrap_or(0)
1224 }
1225}
1226#[allow(dead_code)]
1227#[derive(Debug, Clone)]
1228pub struct SpecPassConfig {
1229 pub phase: SpecPassPhase,
1230 pub enabled: bool,
1231 pub max_iterations: u32,
1232 pub debug_output: bool,
1233 pub pass_name: String,
1234}
1235impl SpecPassConfig {
1236 #[allow(dead_code)]
1237 pub fn new(name: impl Into<String>, phase: SpecPassPhase) -> Self {
1238 SpecPassConfig {
1239 phase,
1240 enabled: true,
1241 max_iterations: 10,
1242 debug_output: false,
1243 pass_name: name.into(),
1244 }
1245 }
1246 #[allow(dead_code)]
1247 pub fn disabled(mut self) -> Self {
1248 self.enabled = false;
1249 self
1250 }
1251 #[allow(dead_code)]
1252 pub fn with_debug(mut self) -> Self {
1253 self.debug_output = true;
1254 self
1255 }
1256 #[allow(dead_code)]
1257 pub fn max_iter(mut self, n: u32) -> Self {
1258 self.max_iterations = n;
1259 self
1260 }
1261}
1262#[allow(dead_code)]
1263#[derive(Debug, Clone)]
1264pub struct SpecLivenessInfo {
1265 pub live_in: Vec<std::collections::HashSet<u32>>,
1266 pub live_out: Vec<std::collections::HashSet<u32>>,
1267 pub defs: Vec<std::collections::HashSet<u32>>,
1268 pub uses: Vec<std::collections::HashSet<u32>>,
1269}
1270impl SpecLivenessInfo {
1271 #[allow(dead_code)]
1272 pub fn new(block_count: usize) -> Self {
1273 SpecLivenessInfo {
1274 live_in: vec![std::collections::HashSet::new(); block_count],
1275 live_out: vec![std::collections::HashSet::new(); block_count],
1276 defs: vec![std::collections::HashSet::new(); block_count],
1277 uses: vec![std::collections::HashSet::new(); block_count],
1278 }
1279 }
1280 #[allow(dead_code)]
1281 pub fn add_def(&mut self, block: usize, var: u32) {
1282 if block < self.defs.len() {
1283 self.defs[block].insert(var);
1284 }
1285 }
1286 #[allow(dead_code)]
1287 pub fn add_use(&mut self, block: usize, var: u32) {
1288 if block < self.uses.len() {
1289 self.uses[block].insert(var);
1290 }
1291 }
1292 #[allow(dead_code)]
1293 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1294 self.live_in
1295 .get(block)
1296 .map(|s| s.contains(&var))
1297 .unwrap_or(false)
1298 }
1299 #[allow(dead_code)]
1300 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1301 self.live_out
1302 .get(block)
1303 .map(|s| s.contains(&var))
1304 .unwrap_or(false)
1305 }
1306}
1307#[allow(dead_code)]
1309#[derive(Debug, Clone)]
1310pub struct SpecExtPassConfig {
1311 pub name: String,
1312 pub phase: SpecExtPassPhase,
1313 pub enabled: bool,
1314 pub max_iterations: usize,
1315 pub debug: u32,
1316 pub timeout_ms: Option<u64>,
1317}
1318impl SpecExtPassConfig {
1319 #[allow(dead_code)]
1320 pub fn new(name: impl Into<String>) -> Self {
1321 Self {
1322 name: name.into(),
1323 phase: SpecExtPassPhase::Middle,
1324 enabled: true,
1325 max_iterations: 100,
1326 debug: 0,
1327 timeout_ms: None,
1328 }
1329 }
1330 #[allow(dead_code)]
1331 pub fn with_phase(mut self, phase: SpecExtPassPhase) -> Self {
1332 self.phase = phase;
1333 self
1334 }
1335 #[allow(dead_code)]
1336 pub fn with_max_iter(mut self, n: usize) -> Self {
1337 self.max_iterations = n;
1338 self
1339 }
1340 #[allow(dead_code)]
1341 pub fn with_debug(mut self, d: u32) -> Self {
1342 self.debug = d;
1343 self
1344 }
1345 #[allow(dead_code)]
1346 pub fn disabled(mut self) -> Self {
1347 self.enabled = false;
1348 self
1349 }
1350 #[allow(dead_code)]
1351 pub fn with_timeout(mut self, ms: u64) -> Self {
1352 self.timeout_ms = Some(ms);
1353 self
1354 }
1355 #[allow(dead_code)]
1356 pub fn is_debug_enabled(&self) -> bool {
1357 self.debug > 0
1358 }
1359}
1360#[allow(dead_code)]
1361#[derive(Debug, Clone)]
1362pub struct SpecWorklist {
1363 pub(super) items: std::collections::VecDeque<u32>,
1364 pub(super) in_worklist: std::collections::HashSet<u32>,
1365}
1366impl SpecWorklist {
1367 #[allow(dead_code)]
1368 pub fn new() -> Self {
1369 SpecWorklist {
1370 items: std::collections::VecDeque::new(),
1371 in_worklist: std::collections::HashSet::new(),
1372 }
1373 }
1374 #[allow(dead_code)]
1375 pub fn push(&mut self, item: u32) -> bool {
1376 if self.in_worklist.insert(item) {
1377 self.items.push_back(item);
1378 true
1379 } else {
1380 false
1381 }
1382 }
1383 #[allow(dead_code)]
1384 pub fn pop(&mut self) -> Option<u32> {
1385 let item = self.items.pop_front()?;
1386 self.in_worklist.remove(&item);
1387 Some(item)
1388 }
1389 #[allow(dead_code)]
1390 pub fn is_empty(&self) -> bool {
1391 self.items.is_empty()
1392 }
1393 #[allow(dead_code)]
1394 pub fn len(&self) -> usize {
1395 self.items.len()
1396 }
1397 #[allow(dead_code)]
1398 pub fn contains(&self, item: u32) -> bool {
1399 self.in_worklist.contains(&item)
1400 }
1401}
1402#[allow(dead_code)]
1404#[derive(Debug, Clone)]
1405pub struct SpecExtWorklist {
1406 pub(super) items: std::collections::VecDeque<usize>,
1407 pub(super) present: Vec<bool>,
1408}
1409impl SpecExtWorklist {
1410 #[allow(dead_code)]
1411 pub fn new(capacity: usize) -> Self {
1412 Self {
1413 items: std::collections::VecDeque::new(),
1414 present: vec![false; capacity],
1415 }
1416 }
1417 #[allow(dead_code)]
1418 pub fn push(&mut self, id: usize) {
1419 if id < self.present.len() && !self.present[id] {
1420 self.present[id] = true;
1421 self.items.push_back(id);
1422 }
1423 }
1424 #[allow(dead_code)]
1425 pub fn push_front(&mut self, id: usize) {
1426 if id < self.present.len() && !self.present[id] {
1427 self.present[id] = true;
1428 self.items.push_front(id);
1429 }
1430 }
1431 #[allow(dead_code)]
1432 pub fn pop(&mut self) -> Option<usize> {
1433 let id = self.items.pop_front()?;
1434 if id < self.present.len() {
1435 self.present[id] = false;
1436 }
1437 Some(id)
1438 }
1439 #[allow(dead_code)]
1440 pub fn is_empty(&self) -> bool {
1441 self.items.is_empty()
1442 }
1443 #[allow(dead_code)]
1444 pub fn len(&self) -> usize {
1445 self.items.len()
1446 }
1447 #[allow(dead_code)]
1448 pub fn contains(&self, id: usize) -> bool {
1449 id < self.present.len() && self.present[id]
1450 }
1451 #[allow(dead_code)]
1452 pub fn drain_all(&mut self) -> Vec<usize> {
1453 let v: Vec<usize> = self.items.drain(..).collect();
1454 for &id in &v {
1455 if id < self.present.len() {
1456 self.present[id] = false;
1457 }
1458 }
1459 v
1460 }
1461}
1462#[derive(Debug, Clone, Default)]
1464pub struct SpecializationCache {
1465 pub(super) entries: HashMap<SpecializationKey, String>,
1467 pub(super) total_growth: usize,
1469}
1470impl SpecializationCache {
1471 pub(super) fn new() -> Self {
1472 SpecializationCache {
1473 entries: HashMap::new(),
1474 total_growth: 0,
1475 }
1476 }
1477 pub(super) fn lookup(&self, key: &SpecializationKey) -> Option<&str> {
1478 self.entries.get(key).map(|s| s.as_str())
1479 }
1480 pub(super) fn insert(&mut self, key: SpecializationKey, name: String, growth: usize) {
1481 self.entries.insert(key, name);
1482 self.total_growth += growth;
1483 }
1484 pub(super) fn specialization_count(&self, original: &str) -> usize {
1485 self.entries
1486 .keys()
1487 .filter(|k| k.original == original)
1488 .count()
1489 }
1490}
1491#[derive(Debug, Clone)]
1493pub struct SizeBudget {
1494 pub(super) original_total: usize,
1495 pub(super) max_total: usize,
1496 pub(super) current_growth: usize,
1497}
1498impl SizeBudget {
1499 pub(super) fn new(original_total: usize, growth_factor: f64) -> Self {
1500 SizeBudget {
1501 original_total,
1502 max_total: (original_total as f64 * growth_factor) as usize,
1503 current_growth: 0,
1504 }
1505 }
1506 pub(super) fn can_afford(&self, additional: usize) -> bool {
1507 self.original_total + self.current_growth + additional <= self.max_total
1508 }
1509 pub(super) fn spend(&mut self, amount: usize) {
1510 self.current_growth += amount;
1511 }
1512 pub(super) fn remaining(&self) -> usize {
1513 self.max_total
1514 .saturating_sub(self.original_total + self.current_growth)
1515 }
1516}
1517#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1519pub enum SpecTypeArg {
1520 Concrete(LcnfType),
1522 Poly,
1524}
1525#[allow(dead_code)]
1526#[derive(Debug, Clone)]
1527pub struct SpecDepGraph {
1528 pub(super) nodes: Vec<u32>,
1529 pub(super) edges: Vec<(u32, u32)>,
1530}
1531impl SpecDepGraph {
1532 #[allow(dead_code)]
1533 pub fn new() -> Self {
1534 SpecDepGraph {
1535 nodes: Vec::new(),
1536 edges: Vec::new(),
1537 }
1538 }
1539 #[allow(dead_code)]
1540 pub fn add_node(&mut self, id: u32) {
1541 if !self.nodes.contains(&id) {
1542 self.nodes.push(id);
1543 }
1544 }
1545 #[allow(dead_code)]
1546 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1547 self.add_node(dep);
1548 self.add_node(dependent);
1549 self.edges.push((dep, dependent));
1550 }
1551 #[allow(dead_code)]
1552 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1553 self.edges
1554 .iter()
1555 .filter(|(d, _)| *d == node)
1556 .map(|(_, dep)| *dep)
1557 .collect()
1558 }
1559 #[allow(dead_code)]
1560 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1561 self.edges
1562 .iter()
1563 .filter(|(_, dep)| *dep == node)
1564 .map(|(d, _)| *d)
1565 .collect()
1566 }
1567 #[allow(dead_code)]
1568 pub fn topological_sort(&self) -> Vec<u32> {
1569 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1570 for &n in &self.nodes {
1571 in_degree.insert(n, 0);
1572 }
1573 for (_, dep) in &self.edges {
1574 *in_degree.entry(*dep).or_insert(0) += 1;
1575 }
1576 let mut queue: std::collections::VecDeque<u32> = self
1577 .nodes
1578 .iter()
1579 .filter(|&&n| in_degree[&n] == 0)
1580 .copied()
1581 .collect();
1582 let mut result = Vec::new();
1583 while let Some(node) = queue.pop_front() {
1584 result.push(node);
1585 for dep in self.dependents_of(node) {
1586 let cnt = in_degree.entry(dep).or_insert(0);
1587 *cnt = cnt.saturating_sub(1);
1588 if *cnt == 0 {
1589 queue.push_back(dep);
1590 }
1591 }
1592 }
1593 result
1594 }
1595 #[allow(dead_code)]
1596 pub fn has_cycle(&self) -> bool {
1597 self.topological_sort().len() < self.nodes.len()
1598 }
1599}
1600pub struct NumericSpecializer {
1602 pub(super) numeric_ops: HashSet<String>,
1604}
1605impl NumericSpecializer {
1606 pub(super) fn new() -> Self {
1607 let mut ops = HashSet::new();
1608 ops.insert("Nat.add".to_string());
1609 ops.insert("Nat.sub".to_string());
1610 ops.insert("Nat.mul".to_string());
1611 ops.insert("Nat.div".to_string());
1612 ops.insert("Nat.mod".to_string());
1613 ops.insert("Nat.beq".to_string());
1614 ops.insert("Nat.ble".to_string());
1615 ops.insert("Nat.blt".to_string());
1616 ops.insert("Nat.decEq".to_string());
1617 ops.insert("Nat.zero".to_string());
1618 ops.insert("Nat.succ".to_string());
1619 NumericSpecializer { numeric_ops: ops }
1620 }
1621 pub(super) fn is_numeric_op(&self, name: &str) -> bool {
1622 self.numeric_ops.contains(name)
1623 }
1624 pub(super) fn specialize_nat_to_u64(&self, ty: &LcnfType) -> LcnfType {
1625 match ty {
1626 LcnfType::Nat => LcnfType::Nat,
1627 LcnfType::Fun(params, ret) => {
1628 let new_params: Vec<LcnfType> = params
1629 .iter()
1630 .map(|p| self.specialize_nat_to_u64(p))
1631 .collect();
1632 let new_ret = self.specialize_nat_to_u64(ret);
1633 LcnfType::Fun(new_params, Box::new(new_ret))
1634 }
1635 LcnfType::Ctor(name, args) => {
1636 let new_args: Vec<LcnfType> =
1637 args.iter().map(|a| self.specialize_nat_to_u64(a)).collect();
1638 LcnfType::Ctor(name.clone(), new_args)
1639 }
1640 other => other.clone(),
1641 }
1642 }
1643}
1644#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1646pub struct SpecializationKey {
1647 pub original: String,
1649 pub type_args: Vec<SpecTypeArg>,
1651 pub const_args: Vec<SpecConstArg>,
1653 pub closure_args: Vec<SpecClosureArg>,
1655}
1656impl SpecializationKey {
1657 pub(super) fn is_trivial(&self) -> bool {
1658 self.type_args
1659 .iter()
1660 .all(|a| matches!(a, SpecTypeArg::Poly))
1661 && self
1662 .const_args
1663 .iter()
1664 .all(|a| matches!(a, SpecConstArg::Unknown))
1665 && self.closure_args.iter().all(|a| a.known_fn.is_none())
1666 }
1667 pub(super) fn mangled_name(&self) -> String {
1668 let mut name = self.original.clone();
1669 for (i, ta) in self.type_args.iter().enumerate() {
1670 if let SpecTypeArg::Concrete(ty) = ta {
1671 name.push_str(&format!("_T{}_{}", i, type_suffix(ty)));
1672 }
1673 }
1674 for (i, ca) in self.const_args.iter().enumerate() {
1675 match ca {
1676 SpecConstArg::Nat(n) => name.push_str(&format!("_C{}_N{}", i, n)),
1677 SpecConstArg::Str(s) => {
1678 let short = if s.len() > 8 { &s[..8] } else { s.as_str() };
1679 name.push_str(&format!("_C{}_S{}", i, short));
1680 }
1681 SpecConstArg::Unknown => {}
1682 }
1683 }
1684 for ca in &self.closure_args {
1685 if let Some(fn_name) = &ca.known_fn {
1686 name.push_str(&format!("_F{}", fn_name));
1687 }
1688 }
1689 name
1690 }
1691}
1692#[allow(dead_code)]
1694#[derive(Debug, Clone, Default)]
1695pub struct SpecExtLiveness {
1696 pub live_in: Vec<Vec<usize>>,
1697 pub live_out: Vec<Vec<usize>>,
1698 pub defs: Vec<Vec<usize>>,
1699 pub uses: Vec<Vec<usize>>,
1700}
1701impl SpecExtLiveness {
1702 #[allow(dead_code)]
1703 pub fn new(n: usize) -> Self {
1704 Self {
1705 live_in: vec![Vec::new(); n],
1706 live_out: vec![Vec::new(); n],
1707 defs: vec![Vec::new(); n],
1708 uses: vec![Vec::new(); n],
1709 }
1710 }
1711 #[allow(dead_code)]
1712 pub fn live_in(&self, b: usize, v: usize) -> bool {
1713 self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1714 }
1715 #[allow(dead_code)]
1716 pub fn live_out(&self, b: usize, v: usize) -> bool {
1717 self.live_out
1718 .get(b)
1719 .map(|s| s.contains(&v))
1720 .unwrap_or(false)
1721 }
1722 #[allow(dead_code)]
1723 pub fn add_def(&mut self, b: usize, v: usize) {
1724 if let Some(s) = self.defs.get_mut(b) {
1725 if !s.contains(&v) {
1726 s.push(v);
1727 }
1728 }
1729 }
1730 #[allow(dead_code)]
1731 pub fn add_use(&mut self, b: usize, v: usize) {
1732 if let Some(s) = self.uses.get_mut(b) {
1733 if !s.contains(&v) {
1734 s.push(v);
1735 }
1736 }
1737 }
1738 #[allow(dead_code)]
1739 pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
1740 self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1741 }
1742 #[allow(dead_code)]
1743 pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
1744 self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
1745 }
1746}
1747#[allow(dead_code)]
1749#[derive(Debug, Clone, Default)]
1750pub struct SpecExtPassStats {
1751 pub iterations: usize,
1752 pub changed: bool,
1753 pub nodes_visited: usize,
1754 pub nodes_modified: usize,
1755 pub time_ms: u64,
1756 pub memory_bytes: usize,
1757 pub errors: usize,
1758}
1759impl SpecExtPassStats {
1760 #[allow(dead_code)]
1761 pub fn new() -> Self {
1762 Self::default()
1763 }
1764 #[allow(dead_code)]
1765 pub fn visit(&mut self) {
1766 self.nodes_visited += 1;
1767 }
1768 #[allow(dead_code)]
1769 pub fn modify(&mut self) {
1770 self.nodes_modified += 1;
1771 self.changed = true;
1772 }
1773 #[allow(dead_code)]
1774 pub fn iterate(&mut self) {
1775 self.iterations += 1;
1776 }
1777 #[allow(dead_code)]
1778 pub fn error(&mut self) {
1779 self.errors += 1;
1780 }
1781 #[allow(dead_code)]
1782 pub fn efficiency(&self) -> f64 {
1783 if self.nodes_visited == 0 {
1784 0.0
1785 } else {
1786 self.nodes_modified as f64 / self.nodes_visited as f64
1787 }
1788 }
1789 #[allow(dead_code)]
1790 pub fn merge(&mut self, o: &SpecExtPassStats) {
1791 self.iterations += o.iterations;
1792 self.changed |= o.changed;
1793 self.nodes_visited += o.nodes_visited;
1794 self.nodes_modified += o.nodes_modified;
1795 self.time_ms += o.time_ms;
1796 self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1797 self.errors += o.errors;
1798 }
1799}
1800#[derive(Debug, Clone)]
1802pub struct SpecCallSite {
1803 pub(super) callee: String,
1805 pub(super) call_idx: usize,
1807 pub(super) type_args: Vec<SpecTypeArg>,
1809 pub(super) const_args: Vec<SpecConstArg>,
1811 pub(super) closure_args: Vec<SpecClosureArg>,
1813 pub(super) callee_var: Option<LcnfVarId>,
1815}
1816#[allow(dead_code)]
1817#[derive(Debug, Clone)]
1818pub struct SpecAnalysisCache {
1819 pub(super) entries: std::collections::HashMap<String, SpecCacheEntry>,
1820 pub(super) max_size: usize,
1821 pub(super) hits: u64,
1822 pub(super) misses: u64,
1823}
1824impl SpecAnalysisCache {
1825 #[allow(dead_code)]
1826 pub fn new(max_size: usize) -> Self {
1827 SpecAnalysisCache {
1828 entries: std::collections::HashMap::new(),
1829 max_size,
1830 hits: 0,
1831 misses: 0,
1832 }
1833 }
1834 #[allow(dead_code)]
1835 pub fn get(&mut self, key: &str) -> Option<&SpecCacheEntry> {
1836 if self.entries.contains_key(key) {
1837 self.hits += 1;
1838 self.entries.get(key)
1839 } else {
1840 self.misses += 1;
1841 None
1842 }
1843 }
1844 #[allow(dead_code)]
1845 pub fn insert(&mut self, key: String, data: Vec<u8>) {
1846 if self.entries.len() >= self.max_size {
1847 if let Some(oldest) = self.entries.keys().next().cloned() {
1848 self.entries.remove(&oldest);
1849 }
1850 }
1851 self.entries.insert(
1852 key.clone(),
1853 SpecCacheEntry {
1854 key,
1855 data,
1856 timestamp: 0,
1857 valid: true,
1858 },
1859 );
1860 }
1861 #[allow(dead_code)]
1862 pub fn invalidate(&mut self, key: &str) {
1863 if let Some(entry) = self.entries.get_mut(key) {
1864 entry.valid = false;
1865 }
1866 }
1867 #[allow(dead_code)]
1868 pub fn clear(&mut self) {
1869 self.entries.clear();
1870 }
1871 #[allow(dead_code)]
1872 pub fn hit_rate(&self) -> f64 {
1873 let total = self.hits + self.misses;
1874 if total == 0 {
1875 return 0.0;
1876 }
1877 self.hits as f64 / total as f64
1878 }
1879 #[allow(dead_code)]
1880 pub fn size(&self) -> usize {
1881 self.entries.len()
1882 }
1883}