1use super::functions::trampoline;
6use std::collections::{HashMap, HashSet};
7
8#[allow(dead_code)]
10pub enum MultiStep<State, Output> {
11 Even(State),
13 Odd(State),
15 Done(Output),
17}
18#[allow(dead_code)]
20pub struct LoopDetector {
21 seen: std::collections::HashSet<u64>,
23 pub checked: u64,
25}
26impl LoopDetector {
27 #[allow(dead_code)]
29 pub fn new() -> Self {
30 Self {
31 seen: std::collections::HashSet::new(),
32 checked: 0,
33 }
34 }
35 #[allow(dead_code)]
38 pub fn check(&mut self, fingerprint: u64) -> bool {
39 self.checked += 1;
40 !self.seen.insert(fingerprint)
41 }
42 #[allow(dead_code)]
44 pub fn reset(&mut self) {
45 self.seen.clear();
46 self.checked = 0;
47 }
48 #[allow(dead_code)]
50 pub fn unique_states(&self) -> usize {
51 self.seen.len()
52 }
53}
54#[allow(dead_code)]
56#[derive(Default)]
57pub struct TrampolineMetricsRegistry {
58 pub functions: std::collections::HashMap<String, FunctionTcoMetrics>,
60}
61impl TrampolineMetricsRegistry {
62 #[allow(dead_code)]
64 pub fn new() -> Self {
65 Self::default()
66 }
67 #[allow(dead_code)]
69 pub fn record(&mut self, function: &str, depth: u64) {
70 self.functions
71 .entry(function.to_string())
72 .or_insert_with(|| FunctionTcoMetrics::new(function))
73 .record(depth);
74 }
75 #[allow(dead_code)]
77 pub fn top_by_steps(&self, n: usize) -> Vec<&FunctionTcoMetrics> {
78 let mut sorted: Vec<&FunctionTcoMetrics> = self.functions.values().collect();
79 sorted.sort_by_key(|b| std::cmp::Reverse(b.total_steps));
80 sorted.truncate(n);
81 sorted
82 }
83 #[allow(dead_code)]
85 pub fn total_calls(&self) -> u64 {
86 self.functions.values().map(|m| m.call_count).sum()
87 }
88}
89#[allow(dead_code)]
91pub struct BoundedTrampoline {
92 pub step_limit: u64,
94}
95impl BoundedTrampoline {
96 #[allow(dead_code)]
98 pub fn new(step_limit: u64) -> Self {
99 Self { step_limit }
100 }
101 #[allow(dead_code)]
104 pub fn run<T>(&self, mut step: TailCall<T>) -> Result<T, String> {
105 let mut count = 0u64;
106 loop {
107 match step {
108 TailCall::Done(v) => return Ok(v),
109 TailCall::Call(f) => {
110 count += 1;
111 if count > self.step_limit {
112 return Err(format!(
113 "trampoline step limit {} exceeded at step {}",
114 self.step_limit, count
115 ));
116 }
117 step = f();
118 }
119 }
120 }
121 }
122}
123#[allow(dead_code)]
125pub struct TailCallScheduler<T> {
126 pending: Option<TailCall<T>>,
128 pub config: TailCallSchedulerConfig,
130 pub total_steps: u64,
132}
133impl<T> TailCallScheduler<T> {
134 #[allow(dead_code)]
136 pub fn new(step: TailCall<T>) -> Self {
137 Self {
138 pending: Some(step),
139 config: TailCallSchedulerConfig::default(),
140 total_steps: 0,
141 }
142 }
143 #[allow(dead_code)]
145 pub fn with_config(step: TailCall<T>, config: TailCallSchedulerConfig) -> Self {
146 Self {
147 pending: Some(step),
148 config,
149 total_steps: 0,
150 }
151 }
152 #[allow(dead_code)]
154 pub fn tick(&mut self) -> SchedulerTickResult<T> {
155 let batch = self.config.max_steps_per_batch;
156 for _ in 0..batch {
157 match self.pending.take() {
158 None => return SchedulerTickResult::Pending,
159 Some(TailCall::Done(v)) => return SchedulerTickResult::Finished(v),
160 Some(TailCall::Call(f)) => {
161 self.total_steps += 1;
162 if self.total_steps > self.config.step_limit {
163 return SchedulerTickResult::StepLimitExceeded;
164 }
165 self.pending = Some(f());
166 }
167 }
168 }
169 SchedulerTickResult::Pending
170 }
171 #[allow(dead_code)]
173 pub fn run_to_completion(mut self) -> Result<T, String> {
174 loop {
175 match self.tick() {
176 SchedulerTickResult::Finished(v) => return Ok(v),
177 SchedulerTickResult::StepLimitExceeded => {
178 return Err(format!("step limit {} exceeded", self.config.step_limit));
179 }
180 SchedulerTickResult::Pending => {}
181 }
182 }
183 }
184}
185#[allow(dead_code)]
187#[derive(Clone, Debug)]
188pub struct UnrollConfig {
189 pub factor: usize,
191 pub full_unroll_limit: usize,
193}
194#[allow(dead_code)]
196#[derive(Clone, Debug)]
197pub struct UnrollResult {
198 pub fully_unrolled: bool,
200 pub factor: usize,
202 pub iterations_saved: usize,
204}
205impl UnrollResult {
206 #[allow(dead_code)]
208 pub fn compute(n: usize, cfg: &UnrollConfig) -> Self {
209 if n <= cfg.full_unroll_limit {
210 UnrollResult {
211 fully_unrolled: true,
212 factor: n,
213 iterations_saved: n,
214 }
215 } else {
216 let factor = cfg.factor.min(n);
217 let remainder = n % factor;
218 let main_iters = n / factor;
219 let saved = n - main_iters - remainder;
220 UnrollResult {
221 fully_unrolled: false,
222 factor,
223 iterations_saved: saved,
224 }
225 }
226 }
227}
228#[allow(dead_code)]
230#[derive(Clone, Debug, Default)]
231pub struct EvaluationContext {
232 pub bindings: std::collections::HashMap<String, u64>,
234 pub depth: u32,
236}
237impl EvaluationContext {
238 #[allow(dead_code)]
240 pub fn new() -> Self {
241 Self::default()
242 }
243 #[allow(dead_code)]
245 pub fn bind(&mut self, name: &str, value: u64) {
246 self.bindings.insert(name.to_string(), value);
247 }
248 #[allow(dead_code)]
250 pub fn lookup(&self, name: &str) -> Option<u64> {
251 self.bindings.get(name).copied()
252 }
253 #[allow(dead_code)]
255 pub fn child(&self) -> Self {
256 Self {
257 bindings: self.bindings.clone(),
258 depth: self.depth + 1,
259 }
260 }
261 #[allow(dead_code)]
263 pub fn size(&self) -> usize {
264 self.bindings.len()
265 }
266}
267#[allow(dead_code)]
269pub struct PeepholeOptimizer {
270 rules: Vec<PeepholeRule>,
271 pub rewrites: usize,
273}
274impl PeepholeOptimizer {
275 #[allow(dead_code)]
277 pub fn new() -> Self {
278 Self {
279 rules: Vec::new(),
280 rewrites: 0,
281 }
282 }
283 #[allow(dead_code)]
285 pub fn add_rule(&mut self, rule: PeepholeRule) {
286 self.rules.push(rule);
287 }
288 #[allow(dead_code)]
290 pub fn optimize<'a>(&mut self, opcodes: &[&'a str]) -> Vec<&'a str> {
291 let mut result: Vec<&'a str> = opcodes.to_vec();
292 let mut changed = true;
293 self.rewrites = 0;
294 while changed {
295 changed = false;
296 'outer: for rule in &self.rules {
297 let plen = rule.pattern.len();
298 if plen == 0 {
299 continue;
300 }
301 let mut i = 0;
302 while i + plen <= result.len() {
303 if result[i..i + plen] == rule.pattern[..] {
304 let mut new_result = result[..i].to_vec();
305 new_result.extend_from_slice(&rule.replacement);
306 new_result.extend_from_slice(&result[i + plen..]);
307 result = new_result;
308 self.rewrites += 1;
309 changed = true;
310 continue 'outer;
311 }
312 i += 1;
313 }
314 }
315 }
316 result
317 }
318}
319pub struct TailCallDetector {
326 pub tail_positions: Vec<usize>,
328}
329impl TailCallDetector {
330 pub fn new() -> Self {
332 TailCallDetector {
333 tail_positions: Vec::new(),
334 }
335 }
336 pub fn analyse(&mut self, opcode_names: &[&str]) {
344 self.tail_positions.clear();
345 for (i, name) in opcode_names.iter().enumerate() {
346 if *name == "Call" {
347 let next = opcode_names.get(i + 1).copied().unwrap_or("");
348 if next == "Return" || next == "Halt" {
349 self.tail_positions.push(i);
350 }
351 }
352 }
353 }
354 pub fn is_tail(&self, idx: usize) -> bool {
356 self.tail_positions.contains(&idx)
357 }
358 pub fn count(&self) -> usize {
360 self.tail_positions.len()
361 }
362}
363#[allow(dead_code)]
365#[derive(Clone, Debug, Default)]
366pub struct TcoStatistics {
367 pub total_runs: u64,
369 pub total_steps: u64,
371 pub global_max_depth: u64,
373 pub step_limit_hits: u64,
375}
376impl TcoStatistics {
377 #[allow(dead_code)]
379 pub fn new() -> Self {
380 Self::default()
381 }
382 #[allow(dead_code)]
384 pub fn record_run(&mut self, counter: &TailCallCounter, hit_limit: bool) {
385 self.total_runs += 1;
386 self.total_steps += counter.optimized;
387 if counter.max_depth > self.global_max_depth {
388 self.global_max_depth = counter.max_depth;
389 }
390 if hit_limit {
391 self.step_limit_hits += 1;
392 }
393 }
394 #[allow(dead_code)]
396 pub fn avg_steps(&self) -> f64 {
397 if self.total_runs == 0 {
398 0.0
399 } else {
400 self.total_steps as f64 / self.total_runs as f64
401 }
402 }
403 #[allow(dead_code)]
405 pub fn summary(&self) -> String {
406 format!(
407 "TcoStats: runs={}, total_steps={}, max_depth={}, limit_hits={}, avg_steps={:.2}",
408 self.total_runs,
409 self.total_steps,
410 self.global_max_depth,
411 self.step_limit_hits,
412 self.avg_steps()
413 )
414 }
415}
416#[allow(dead_code)]
418#[derive(Clone, Debug)]
419pub struct StackFrame {
420 pub function: String,
422 pub locals: Vec<(String, u64)>,
424 pub return_address: usize,
426}
427impl StackFrame {
428 #[allow(dead_code)]
430 pub fn new(function: &str, return_address: usize) -> Self {
431 Self {
432 function: function.to_string(),
433 locals: Vec::new(),
434 return_address,
435 }
436 }
437 #[allow(dead_code)]
439 pub fn bind(&mut self, name: &str, value: u64) {
440 self.locals.push((name.to_string(), value));
441 }
442 #[allow(dead_code)]
444 pub fn lookup(&self, name: &str) -> Option<u64> {
445 self.locals
446 .iter()
447 .rev()
448 .find(|(n, _)| n == name)
449 .map(|(_, v)| *v)
450 }
451}
452#[allow(dead_code)]
454#[derive(Clone, Debug)]
455pub struct InliningThreshold {
456 pub max_size: usize,
458 pub max_depth: usize,
460 pub min_call_count: u64,
462}
463pub struct RecursiveStep;
475impl RecursiveStep {
476 pub fn run<I, A, F>(initial: I, acc: A, step: F) -> A
483 where
484 I: Clone + 'static,
485 A: Clone + 'static,
486 F: Fn(I, A) -> Option<(I, A)> + Clone + 'static,
487 {
488 fn go<
489 I: Clone + 'static,
490 A: Clone + 'static,
491 F: Fn(I, A) -> Option<(I, A)> + Clone + 'static,
492 >(
493 i: I,
494 a: A,
495 f: F,
496 ) -> TailCall<A> {
497 match f(i, a.clone()) {
498 None => TailCall::Done(a),
499 Some((next_i, next_a)) => {
500 let f2 = f.clone();
501 TailCall::Call(Box::new(move || go(next_i, next_a, f2)))
502 }
503 }
504 }
505 trampoline(go(initial, acc, step))
506 }
507}
508#[allow(dead_code)]
510#[derive(Clone, Copy, Debug, PartialEq, Eq)]
511pub enum BinopKind {
512 Add,
513 Sub,
514 Mul,
515 Div,
516}
517impl BinopKind {
518 #[allow(dead_code)]
520 pub fn eval(self, lhs: u64, rhs: u64) -> Option<u64> {
521 match self {
522 BinopKind::Add => Some(lhs.wrapping_add(rhs)),
523 BinopKind::Sub => Some(lhs.wrapping_sub(rhs)),
524 BinopKind::Mul => Some(lhs.wrapping_mul(rhs)),
525 BinopKind::Div => lhs.checked_div(rhs),
526 }
527 }
528}
529pub enum TailCall<T> {
535 Done(T),
537 Call(Box<dyn FnOnce() -> TailCall<T>>),
540}
541pub enum StepResult<State, Output> {
543 Continue(State),
545 Finished(Output),
547 Error(String),
549}
550#[allow(dead_code)]
552pub struct TailCallOptimizer {
553 detector: TailCallDetector,
554 pub stats: TailCallCounter,
556}
557impl TailCallOptimizer {
558 #[allow(dead_code)]
560 pub fn new() -> Self {
561 Self {
562 detector: TailCallDetector::new(),
563 stats: TailCallCounter::new(),
564 }
565 }
566 #[allow(dead_code)]
568 pub fn analyse_chunk(&mut self, opcodes: &[&str]) -> TailCallAnalysisReport {
569 self.detector.analyse(opcodes);
570 let report = TailCallAnalysisReport::build(&self.detector, opcodes);
571 self.stats.optimized += report.tail_positions.len() as u64;
572 report
573 }
574 #[allow(dead_code)]
576 pub fn is_tail_call(&self, idx: usize) -> bool {
577 self.detector.is_tail(idx)
578 }
579}
580#[allow(dead_code)]
582#[derive(Clone, Debug)]
583pub struct TailCallAnalysisReport {
584 pub tail_positions: Vec<usize>,
586 pub total_calls: usize,
588 pub tail_ratio: f64,
590 pub summary: String,
592}
593impl TailCallAnalysisReport {
594 #[allow(dead_code)]
596 pub fn build(detector: &TailCallDetector, opcodes: &[&str]) -> Self {
597 let total_calls = opcodes.iter().filter(|&&op| op == "Call").count();
598 let tail_count = detector.count();
599 let tail_ratio = if total_calls == 0 {
600 0.0
601 } else {
602 tail_count as f64 / total_calls as f64
603 };
604 let summary = format!(
605 "{}/{} calls ({:.0}%) are in tail position",
606 tail_count,
607 total_calls,
608 tail_ratio * 100.0
609 );
610 Self {
611 tail_positions: detector.tail_positions.clone(),
612 total_calls,
613 tail_ratio,
614 summary,
615 }
616 }
617}
618#[allow(dead_code)]
620pub struct ExtendedTailCallDetector {
621 pub current_function: String,
623 pub classified: Vec<(usize, TailPositionKind)>,
625}
626impl ExtendedTailCallDetector {
627 #[allow(dead_code)]
629 pub fn new(function_name: &str) -> Self {
630 Self {
631 current_function: function_name.to_string(),
632 classified: Vec::new(),
633 }
634 }
635 #[allow(dead_code)]
637 pub fn analyse_with_callees(&mut self, ops: &[(&str, Option<&str>)]) {
638 self.classified.clear();
639 for (i, (op, callee)) in ops.iter().enumerate() {
640 if *op != "Call" {
641 continue;
642 }
643 let next = ops.get(i + 1).map(|(o, _)| *o).unwrap_or("");
644 if next != "Return" && next != "Halt" {
645 self.classified.push((i, TailPositionKind::NonTail));
646 continue;
647 }
648 let kind = match *callee {
649 Some(name) if name == self.current_function => TailPositionKind::SelfTailCall,
650 Some(_) => TailPositionKind::MutualTailCall,
651 None => TailPositionKind::ExternalTailCall,
652 };
653 self.classified.push((i, kind));
654 }
655 }
656 #[allow(dead_code)]
658 pub fn count_kind(&self, kind: TailPositionKind) -> usize {
659 self.classified.iter().filter(|(_, k)| *k == kind).count()
660 }
661}
662#[allow(dead_code)]
664#[derive(Clone, Copy, Debug, PartialEq, Eq)]
665pub enum TailPositionKind {
666 SelfTailCall,
668 MutualTailCall,
670 ExternalTailCall,
672 NonTail,
674}
675#[allow(dead_code)]
677pub enum Thunk<T> {
678 Deferred(Box<dyn FnOnce() -> T>),
680 Evaluated(T),
682}
683impl<T: Clone> Thunk<T> {
684 #[allow(dead_code)]
686 pub fn defer(f: impl FnOnce() -> T + 'static) -> Self {
687 Thunk::Deferred(Box::new(f))
688 }
689 #[allow(dead_code)]
691 pub fn force(&mut self) -> &T {
692 match self {
693 Thunk::Evaluated(ref v) => v,
694 Thunk::Deferred(_) => {
695 let Thunk::Deferred(f) =
696 std::mem::replace(self, Thunk::Evaluated(unsafe { std::mem::zeroed() }))
697 else {
698 unreachable!()
699 };
700 let val = f();
701 *self = Thunk::Evaluated(val);
702 match self {
703 Thunk::Evaluated(ref v) => v,
704 _ => unreachable!(),
705 }
706 }
707 }
708 }
709}
710#[allow(dead_code)]
712#[derive(Clone, Debug)]
713pub struct RewriteRule {
714 pub lhs: String,
716 pub rhs: String,
718 pub unconditional: bool,
720}
721impl RewriteRule {
722 #[allow(dead_code)]
724 pub fn new(lhs: &str, rhs: &str) -> Self {
725 Self {
726 lhs: lhs.to_string(),
727 rhs: rhs.to_string(),
728 unconditional: true,
729 }
730 }
731 #[allow(dead_code)]
733 pub fn conditional(lhs: &str, rhs: &str) -> Self {
734 Self {
735 lhs: lhs.to_string(),
736 rhs: rhs.to_string(),
737 unconditional: false,
738 }
739 }
740}
741#[allow(dead_code)]
743#[derive(Clone, Debug, Default)]
744pub struct FunctionTcoMetrics {
745 pub name: String,
747 pub call_count: u64,
749 pub max_depth_eliminated: u64,
751 pub total_steps: u64,
753}
754impl FunctionTcoMetrics {
755 #[allow(dead_code)]
757 pub fn new(name: &str) -> Self {
758 Self {
759 name: name.to_string(),
760 ..Default::default()
761 }
762 }
763 #[allow(dead_code)]
765 pub fn record(&mut self, depth: u64) {
766 self.call_count += 1;
767 self.total_steps += depth;
768 if depth > self.max_depth_eliminated {
769 self.max_depth_eliminated = depth;
770 }
771 }
772 #[allow(dead_code)]
774 pub fn avg_depth(&self) -> f64 {
775 if self.call_count == 0 {
776 0.0
777 } else {
778 self.total_steps as f64 / self.call_count as f64
779 }
780 }
781}
782#[allow(dead_code)]
784#[derive(Clone, Debug)]
785pub struct TailCallChain {
786 pub functions: Vec<String>,
788 pub can_fuse: bool,
790}
791impl TailCallChain {
792 #[allow(dead_code)]
794 pub fn new() -> Self {
795 Self {
796 functions: Vec::new(),
797 can_fuse: true,
798 }
799 }
800 #[allow(dead_code)]
802 pub fn push(&mut self, name: &str) {
803 self.functions.push(name.to_string());
804 }
805 #[allow(dead_code)]
807 pub fn mark_non_fusable(&mut self) {
808 self.can_fuse = false;
809 }
810 #[allow(dead_code)]
812 pub fn len(&self) -> usize {
813 self.functions.len()
814 }
815 #[allow(dead_code)]
817 pub fn is_empty(&self) -> bool {
818 self.functions.is_empty()
819 }
820}
821#[allow(dead_code)]
823pub struct TailCallOptimizationPass {
824 pub optimizer: TailCallOptimizer,
826 pub processed: Vec<String>,
828 pub skipped: Vec<String>,
830}
831impl TailCallOptimizationPass {
832 #[allow(dead_code)]
834 pub fn new() -> Self {
835 Self {
836 optimizer: TailCallOptimizer::new(),
837 processed: Vec::new(),
838 skipped: Vec::new(),
839 }
840 }
841 #[allow(dead_code)]
844 pub fn process_function(
845 &mut self,
846 name: &str,
847 opcodes: &[&str],
848 skip: bool,
849 ) -> Option<TailCallAnalysisReport> {
850 if skip {
851 self.skipped.push(name.to_string());
852 return None;
853 }
854 let report = self.optimizer.analyse_chunk(opcodes);
855 self.processed.push(name.to_string());
856 Some(report)
857 }
858 #[allow(dead_code)]
860 pub fn summary(&self) -> String {
861 format!(
862 "TCO pass: {} processed, {} skipped, {} tail calls found",
863 self.processed.len(),
864 self.skipped.len(),
865 self.optimizer.stats.optimized
866 )
867 }
868}
869#[derive(Clone, Debug, Default)]
871pub struct TailCallCounter {
872 pub optimized: u64,
874 pub max_depth: u64,
876}
877impl TailCallCounter {
878 pub fn new() -> Self {
880 Self::default()
881 }
882 pub fn record(&mut self, depth: u64) {
884 self.optimized += 1;
885 if depth > self.max_depth {
886 self.max_depth = depth;
887 }
888 }
889}
890#[allow(dead_code)]
892#[derive(Clone, Copy, Debug, PartialEq, Eq)]
893pub enum InliningDecision {
894 Inline,
896 DoNotInline,
898 ForceInline,
900}
901#[allow(dead_code)]
903#[derive(Clone, Debug)]
904pub struct TailCallSchedulerConfig {
905 pub max_steps_per_batch: usize,
907 pub step_limit: u64,
909}
910#[allow(dead_code)]
912#[derive(Clone, Debug)]
913pub struct TailCallBenchmarkResult {
914 pub name: String,
916 pub iterations: u64,
918 pub total_ns: u64,
920 pub value: u64,
922}
923impl TailCallBenchmarkResult {
924 #[allow(dead_code)]
926 pub fn throughput(&self) -> f64 {
927 if self.total_ns == 0 {
928 0.0
929 } else {
930 self.iterations as f64 / (self.total_ns as f64 / 1_000_000_000.0)
931 }
932 }
933 #[allow(dead_code)]
935 pub fn report(&self) -> String {
936 format!(
937 "Benchmark[{}]: {} iters, {} ns, value={}",
938 self.name, self.iterations, self.total_ns, self.value
939 )
940 }
941}
942#[allow(dead_code)]
944pub struct PeepholeRule {
945 pub pattern: Vec<&'static str>,
947 pub replacement: Vec<&'static str>,
949 pub description: &'static str,
951}
952impl PeepholeRule {
953 #[allow(dead_code)]
955 pub fn new(
956 pattern: Vec<&'static str>,
957 replacement: Vec<&'static str>,
958 description: &'static str,
959 ) -> Self {
960 Self {
961 pattern,
962 replacement,
963 description,
964 }
965 }
966}
967#[allow(dead_code)]
969#[derive(Clone, Debug)]
970pub struct TailCallProof {
971 pub function_name: String,
973 pub decreasing_argument: String,
975 pub ordering: String,
977 pub justification: String,
979}
980impl TailCallProof {
981 #[allow(dead_code)]
983 pub fn new(
984 function_name: &str,
985 decreasing_argument: &str,
986 ordering: &str,
987 justification: &str,
988 ) -> Self {
989 Self {
990 function_name: function_name.to_string(),
991 decreasing_argument: decreasing_argument.to_string(),
992 ordering: ordering.to_string(),
993 justification: justification.to_string(),
994 }
995 }
996 #[allow(dead_code)]
998 pub fn format(&self) -> String {
999 format!(
1000 "TCO Proof for `{}`:\n Decreasing: {}\n Ordering: {}\n Justification: {}",
1001 self.function_name, self.decreasing_argument, self.ordering, self.justification
1002 )
1003 }
1004}
1005#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1007pub enum TailPosition {
1008 Tail,
1010 NonTail,
1012}
1013#[allow(dead_code)]
1015pub struct ExplicitCallStack {
1016 frames: Vec<StackFrame>,
1017 pub max_depth: usize,
1019}
1020impl ExplicitCallStack {
1021 #[allow(dead_code)]
1023 pub fn new() -> Self {
1024 Self {
1025 frames: Vec::new(),
1026 max_depth: 0,
1027 }
1028 }
1029 #[allow(dead_code)]
1031 pub fn push(&mut self, frame: StackFrame) {
1032 self.frames.push(frame);
1033 if self.frames.len() > self.max_depth {
1034 self.max_depth = self.frames.len();
1035 }
1036 }
1037 #[allow(dead_code)]
1039 pub fn pop(&mut self) -> Option<StackFrame> {
1040 self.frames.pop()
1041 }
1042 #[allow(dead_code)]
1044 pub fn depth(&self) -> usize {
1045 self.frames.len()
1046 }
1047 #[allow(dead_code)]
1049 pub fn top(&self) -> Option<&StackFrame> {
1050 self.frames.last()
1051 }
1052 #[allow(dead_code)]
1054 pub fn top_mut(&mut self) -> Option<&mut StackFrame> {
1055 self.frames.last_mut()
1056 }
1057 #[allow(dead_code)]
1059 pub fn backtrace(&self) -> Vec<&str> {
1060 self.frames.iter().map(|f| f.function.as_str()).collect()
1061 }
1062 #[allow(dead_code)]
1064 pub fn format_backtrace(&self) -> String {
1065 let mut out = String::from("Backtrace (most recent last):\n");
1066 for (i, frame) in self.frames.iter().enumerate() {
1067 out.push_str(&format!(
1068 " {:3}: {} @ {}\n",
1069 i, frame.function, frame.return_address
1070 ));
1071 }
1072 out
1073 }
1074}
1075#[allow(dead_code)]
1077pub enum SchedulerTickResult<T> {
1078 Pending,
1080 Finished(T),
1082 StepLimitExceeded,
1084}
1085#[allow(dead_code)]
1090pub enum MutualTailCall<A, B, R> {
1091 GoA(A, Box<dyn FnOnce(A) -> MutualTailCall<A, B, R>>),
1093 GoB(B, Box<dyn FnOnce(B) -> MutualTailCall<A, B, R>>),
1095 Done(R),
1097}
1098#[allow(dead_code)]
1100#[derive(Clone, Debug)]
1101pub enum ContinuationFrame {
1102 ApplyBinop { op: BinopKind, operand: u64 },
1104 StoreResult { var: String },
1106 PrintResult,
1108}
1109#[allow(dead_code)]
1111#[derive(Clone, Debug, PartialEq)]
1112pub enum PartialValue {
1113 Known(u64),
1115 Unknown(String),
1117 Bottom,
1119}
1120impl PartialValue {
1121 #[allow(dead_code)]
1123 pub fn is_known(&self) -> bool {
1124 matches!(self, PartialValue::Known(_))
1125 }
1126 #[allow(dead_code)]
1128 pub fn add(&self, other: &PartialValue) -> PartialValue {
1129 match (self, other) {
1130 (PartialValue::Known(a), PartialValue::Known(b)) => {
1131 PartialValue::Known(a.wrapping_add(*b))
1132 }
1133 (PartialValue::Bottom, _) | (_, PartialValue::Bottom) => PartialValue::Bottom,
1134 _ => PartialValue::Unknown(format!("{:?} + {:?}", self, other)),
1135 }
1136 }
1137 #[allow(dead_code)]
1139 pub fn mul(&self, other: &PartialValue) -> PartialValue {
1140 match (self, other) {
1141 (PartialValue::Known(a), PartialValue::Known(b)) => {
1142 PartialValue::Known(a.wrapping_mul(*b))
1143 }
1144 (PartialValue::Known(0), _) | (_, PartialValue::Known(0)) => PartialValue::Known(0),
1145 (PartialValue::Bottom, _) | (_, PartialValue::Bottom) => PartialValue::Bottom,
1146 _ => PartialValue::Unknown(format!("{:?} * {:?}", self, other)),
1147 }
1148 }
1149}
1150#[allow(dead_code)]
1152#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1153pub struct StateMachineState {
1154 pub id: u32,
1156 pub output: Vec<String>,
1158}
1159impl StateMachineState {
1160 #[allow(dead_code)]
1162 pub fn new(id: u32) -> Self {
1163 Self {
1164 id,
1165 output: Vec::new(),
1166 }
1167 }
1168 #[allow(dead_code)]
1170 pub fn emit(mut self, s: &str) -> Self {
1171 self.output.push(s.to_string());
1172 self
1173 }
1174}
1175#[allow(dead_code)]
1177pub struct ContinuationEvaluator {
1178 pub values: Vec<u64>,
1180 pub continuations: Vec<ContinuationFrame>,
1182 pub env: std::collections::HashMap<String, u64>,
1184}
1185impl ContinuationEvaluator {
1186 #[allow(dead_code)]
1188 pub fn new() -> Self {
1189 Self {
1190 values: Vec::new(),
1191 continuations: Vec::new(),
1192 env: std::collections::HashMap::new(),
1193 }
1194 }
1195 #[allow(dead_code)]
1197 pub fn push_value(&mut self, v: u64) {
1198 self.values.push(v);
1199 }
1200 #[allow(dead_code)]
1202 pub fn pop_value(&mut self) -> Option<u64> {
1203 self.values.pop()
1204 }
1205 #[allow(dead_code)]
1207 pub fn push_cont(&mut self, frame: ContinuationFrame) {
1208 self.continuations.push(frame);
1209 }
1210 #[allow(dead_code)]
1212 pub fn step(&mut self) -> bool {
1213 let frame = match self.continuations.pop() {
1214 Some(f) => f,
1215 None => return false,
1216 };
1217 match frame {
1218 ContinuationFrame::ApplyBinop { op, operand } => {
1219 if let Some(lhs) = self.values.pop() {
1220 if let Some(result) = op.eval(lhs, operand) {
1221 self.values.push(result);
1222 }
1223 }
1224 }
1225 ContinuationFrame::StoreResult { var } => {
1226 if let Some(v) = self.values.last().copied() {
1227 self.env.insert(var, v);
1228 }
1229 }
1230 ContinuationFrame::PrintResult => {}
1231 }
1232 true
1233 }
1234 #[allow(dead_code)]
1236 pub fn run(&mut self) {
1237 while self.step() {}
1238 }
1239}