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(|a, b| b.total_steps.cmp(&a.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 => {
526 if rhs == 0 {
527 None
528 } else {
529 Some(lhs / rhs)
530 }
531 }
532 }
533 }
534}
535pub enum TailCall<T> {
541 Done(T),
543 Call(Box<dyn FnOnce() -> TailCall<T>>),
546}
547pub enum StepResult<State, Output> {
549 Continue(State),
551 Finished(Output),
553 Error(String),
555}
556#[allow(dead_code)]
558pub struct TailCallOptimizer {
559 detector: TailCallDetector,
560 pub stats: TailCallCounter,
562}
563impl TailCallOptimizer {
564 #[allow(dead_code)]
566 pub fn new() -> Self {
567 Self {
568 detector: TailCallDetector::new(),
569 stats: TailCallCounter::new(),
570 }
571 }
572 #[allow(dead_code)]
574 pub fn analyse_chunk(&mut self, opcodes: &[&str]) -> TailCallAnalysisReport {
575 self.detector.analyse(opcodes);
576 let report = TailCallAnalysisReport::build(&self.detector, opcodes);
577 self.stats.optimized += report.tail_positions.len() as u64;
578 report
579 }
580 #[allow(dead_code)]
582 pub fn is_tail_call(&self, idx: usize) -> bool {
583 self.detector.is_tail(idx)
584 }
585}
586#[allow(dead_code)]
588#[derive(Clone, Debug)]
589pub struct TailCallAnalysisReport {
590 pub tail_positions: Vec<usize>,
592 pub total_calls: usize,
594 pub tail_ratio: f64,
596 pub summary: String,
598}
599impl TailCallAnalysisReport {
600 #[allow(dead_code)]
602 pub fn build(detector: &TailCallDetector, opcodes: &[&str]) -> Self {
603 let total_calls = opcodes.iter().filter(|&&op| op == "Call").count();
604 let tail_count = detector.count();
605 let tail_ratio = if total_calls == 0 {
606 0.0
607 } else {
608 tail_count as f64 / total_calls as f64
609 };
610 let summary = format!(
611 "{}/{} calls ({:.0}%) are in tail position",
612 tail_count,
613 total_calls,
614 tail_ratio * 100.0
615 );
616 Self {
617 tail_positions: detector.tail_positions.clone(),
618 total_calls,
619 tail_ratio,
620 summary,
621 }
622 }
623}
624#[allow(dead_code)]
626pub struct ExtendedTailCallDetector {
627 pub current_function: String,
629 pub classified: Vec<(usize, TailPositionKind)>,
631}
632impl ExtendedTailCallDetector {
633 #[allow(dead_code)]
635 pub fn new(function_name: &str) -> Self {
636 Self {
637 current_function: function_name.to_string(),
638 classified: Vec::new(),
639 }
640 }
641 #[allow(dead_code)]
643 pub fn analyse_with_callees(&mut self, ops: &[(&str, Option<&str>)]) {
644 self.classified.clear();
645 for (i, (op, callee)) in ops.iter().enumerate() {
646 if *op != "Call" {
647 continue;
648 }
649 let next = ops.get(i + 1).map(|(o, _)| *o).unwrap_or("");
650 if next != "Return" && next != "Halt" {
651 self.classified.push((i, TailPositionKind::NonTail));
652 continue;
653 }
654 let kind = match *callee {
655 Some(name) if name == self.current_function => TailPositionKind::SelfTailCall,
656 Some(_) => TailPositionKind::MutualTailCall,
657 None => TailPositionKind::ExternalTailCall,
658 };
659 self.classified.push((i, kind));
660 }
661 }
662 #[allow(dead_code)]
664 pub fn count_kind(&self, kind: TailPositionKind) -> usize {
665 self.classified.iter().filter(|(_, k)| *k == kind).count()
666 }
667}
668#[allow(dead_code)]
670#[derive(Clone, Copy, Debug, PartialEq, Eq)]
671pub enum TailPositionKind {
672 SelfTailCall,
674 MutualTailCall,
676 ExternalTailCall,
678 NonTail,
680}
681#[allow(dead_code)]
683pub enum Thunk<T> {
684 Deferred(Box<dyn FnOnce() -> T>),
686 Evaluated(T),
688}
689impl<T: Clone> Thunk<T> {
690 #[allow(dead_code)]
692 pub fn defer(f: impl FnOnce() -> T + 'static) -> Self {
693 Thunk::Deferred(Box::new(f))
694 }
695 #[allow(dead_code)]
697 pub fn force(&mut self) -> &T {
698 match self {
699 Thunk::Evaluated(ref v) => v,
700 Thunk::Deferred(_) => {
701 let Thunk::Deferred(f) =
702 std::mem::replace(self, Thunk::Evaluated(unsafe { std::mem::zeroed() }))
703 else {
704 unreachable!()
705 };
706 let val = f();
707 *self = Thunk::Evaluated(val);
708 match self {
709 Thunk::Evaluated(ref v) => v,
710 _ => unreachable!(),
711 }
712 }
713 }
714 }
715}
716#[allow(dead_code)]
718#[derive(Clone, Debug)]
719pub struct RewriteRule {
720 pub lhs: String,
722 pub rhs: String,
724 pub unconditional: bool,
726}
727impl RewriteRule {
728 #[allow(dead_code)]
730 pub fn new(lhs: &str, rhs: &str) -> Self {
731 Self {
732 lhs: lhs.to_string(),
733 rhs: rhs.to_string(),
734 unconditional: true,
735 }
736 }
737 #[allow(dead_code)]
739 pub fn conditional(lhs: &str, rhs: &str) -> Self {
740 Self {
741 lhs: lhs.to_string(),
742 rhs: rhs.to_string(),
743 unconditional: false,
744 }
745 }
746}
747#[allow(dead_code)]
749#[derive(Clone, Debug, Default)]
750pub struct FunctionTcoMetrics {
751 pub name: String,
753 pub call_count: u64,
755 pub max_depth_eliminated: u64,
757 pub total_steps: u64,
759}
760impl FunctionTcoMetrics {
761 #[allow(dead_code)]
763 pub fn new(name: &str) -> Self {
764 Self {
765 name: name.to_string(),
766 ..Default::default()
767 }
768 }
769 #[allow(dead_code)]
771 pub fn record(&mut self, depth: u64) {
772 self.call_count += 1;
773 self.total_steps += depth;
774 if depth > self.max_depth_eliminated {
775 self.max_depth_eliminated = depth;
776 }
777 }
778 #[allow(dead_code)]
780 pub fn avg_depth(&self) -> f64 {
781 if self.call_count == 0 {
782 0.0
783 } else {
784 self.total_steps as f64 / self.call_count as f64
785 }
786 }
787}
788#[allow(dead_code)]
790#[derive(Clone, Debug)]
791pub struct TailCallChain {
792 pub functions: Vec<String>,
794 pub can_fuse: bool,
796}
797impl TailCallChain {
798 #[allow(dead_code)]
800 pub fn new() -> Self {
801 Self {
802 functions: Vec::new(),
803 can_fuse: true,
804 }
805 }
806 #[allow(dead_code)]
808 pub fn push(&mut self, name: &str) {
809 self.functions.push(name.to_string());
810 }
811 #[allow(dead_code)]
813 pub fn mark_non_fusable(&mut self) {
814 self.can_fuse = false;
815 }
816 #[allow(dead_code)]
818 pub fn len(&self) -> usize {
819 self.functions.len()
820 }
821 #[allow(dead_code)]
823 pub fn is_empty(&self) -> bool {
824 self.functions.is_empty()
825 }
826}
827#[allow(dead_code)]
829pub struct TailCallOptimizationPass {
830 pub optimizer: TailCallOptimizer,
832 pub processed: Vec<String>,
834 pub skipped: Vec<String>,
836}
837impl TailCallOptimizationPass {
838 #[allow(dead_code)]
840 pub fn new() -> Self {
841 Self {
842 optimizer: TailCallOptimizer::new(),
843 processed: Vec::new(),
844 skipped: Vec::new(),
845 }
846 }
847 #[allow(dead_code)]
850 pub fn process_function(
851 &mut self,
852 name: &str,
853 opcodes: &[&str],
854 skip: bool,
855 ) -> Option<TailCallAnalysisReport> {
856 if skip {
857 self.skipped.push(name.to_string());
858 return None;
859 }
860 let report = self.optimizer.analyse_chunk(opcodes);
861 self.processed.push(name.to_string());
862 Some(report)
863 }
864 #[allow(dead_code)]
866 pub fn summary(&self) -> String {
867 format!(
868 "TCO pass: {} processed, {} skipped, {} tail calls found",
869 self.processed.len(),
870 self.skipped.len(),
871 self.optimizer.stats.optimized
872 )
873 }
874}
875#[derive(Clone, Debug, Default)]
877pub struct TailCallCounter {
878 pub optimized: u64,
880 pub max_depth: u64,
882}
883impl TailCallCounter {
884 pub fn new() -> Self {
886 Self::default()
887 }
888 pub fn record(&mut self, depth: u64) {
890 self.optimized += 1;
891 if depth > self.max_depth {
892 self.max_depth = depth;
893 }
894 }
895}
896#[allow(dead_code)]
898#[derive(Clone, Copy, Debug, PartialEq, Eq)]
899pub enum InliningDecision {
900 Inline,
902 DoNotInline,
904 ForceInline,
906}
907#[allow(dead_code)]
909#[derive(Clone, Debug)]
910pub struct TailCallSchedulerConfig {
911 pub max_steps_per_batch: usize,
913 pub step_limit: u64,
915}
916#[allow(dead_code)]
918#[derive(Clone, Debug)]
919pub struct TailCallBenchmarkResult {
920 pub name: String,
922 pub iterations: u64,
924 pub total_ns: u64,
926 pub value: u64,
928}
929impl TailCallBenchmarkResult {
930 #[allow(dead_code)]
932 pub fn throughput(&self) -> f64 {
933 if self.total_ns == 0 {
934 0.0
935 } else {
936 self.iterations as f64 / (self.total_ns as f64 / 1_000_000_000.0)
937 }
938 }
939 #[allow(dead_code)]
941 pub fn report(&self) -> String {
942 format!(
943 "Benchmark[{}]: {} iters, {} ns, value={}",
944 self.name, self.iterations, self.total_ns, self.value
945 )
946 }
947}
948#[allow(dead_code)]
950pub struct PeepholeRule {
951 pub pattern: Vec<&'static str>,
953 pub replacement: Vec<&'static str>,
955 pub description: &'static str,
957}
958impl PeepholeRule {
959 #[allow(dead_code)]
961 pub fn new(
962 pattern: Vec<&'static str>,
963 replacement: Vec<&'static str>,
964 description: &'static str,
965 ) -> Self {
966 Self {
967 pattern,
968 replacement,
969 description,
970 }
971 }
972}
973#[allow(dead_code)]
975#[derive(Clone, Debug)]
976pub struct TailCallProof {
977 pub function_name: String,
979 pub decreasing_argument: String,
981 pub ordering: String,
983 pub justification: String,
985}
986impl TailCallProof {
987 #[allow(dead_code)]
989 pub fn new(
990 function_name: &str,
991 decreasing_argument: &str,
992 ordering: &str,
993 justification: &str,
994 ) -> Self {
995 Self {
996 function_name: function_name.to_string(),
997 decreasing_argument: decreasing_argument.to_string(),
998 ordering: ordering.to_string(),
999 justification: justification.to_string(),
1000 }
1001 }
1002 #[allow(dead_code)]
1004 pub fn format(&self) -> String {
1005 format!(
1006 "TCO Proof for `{}`:\n Decreasing: {}\n Ordering: {}\n Justification: {}",
1007 self.function_name, self.decreasing_argument, self.ordering, self.justification
1008 )
1009 }
1010}
1011#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1013pub enum TailPosition {
1014 Tail,
1016 NonTail,
1018}
1019#[allow(dead_code)]
1021pub struct ExplicitCallStack {
1022 frames: Vec<StackFrame>,
1023 pub max_depth: usize,
1025}
1026impl ExplicitCallStack {
1027 #[allow(dead_code)]
1029 pub fn new() -> Self {
1030 Self {
1031 frames: Vec::new(),
1032 max_depth: 0,
1033 }
1034 }
1035 #[allow(dead_code)]
1037 pub fn push(&mut self, frame: StackFrame) {
1038 self.frames.push(frame);
1039 if self.frames.len() > self.max_depth {
1040 self.max_depth = self.frames.len();
1041 }
1042 }
1043 #[allow(dead_code)]
1045 pub fn pop(&mut self) -> Option<StackFrame> {
1046 self.frames.pop()
1047 }
1048 #[allow(dead_code)]
1050 pub fn depth(&self) -> usize {
1051 self.frames.len()
1052 }
1053 #[allow(dead_code)]
1055 pub fn top(&self) -> Option<&StackFrame> {
1056 self.frames.last()
1057 }
1058 #[allow(dead_code)]
1060 pub fn top_mut(&mut self) -> Option<&mut StackFrame> {
1061 self.frames.last_mut()
1062 }
1063 #[allow(dead_code)]
1065 pub fn backtrace(&self) -> Vec<&str> {
1066 self.frames.iter().map(|f| f.function.as_str()).collect()
1067 }
1068 #[allow(dead_code)]
1070 pub fn format_backtrace(&self) -> String {
1071 let mut out = String::from("Backtrace (most recent last):\n");
1072 for (i, frame) in self.frames.iter().enumerate() {
1073 out.push_str(&format!(
1074 " {:3}: {} @ {}\n",
1075 i, frame.function, frame.return_address
1076 ));
1077 }
1078 out
1079 }
1080}
1081#[allow(dead_code)]
1083pub enum SchedulerTickResult<T> {
1084 Pending,
1086 Finished(T),
1088 StepLimitExceeded,
1090}
1091#[allow(dead_code)]
1096pub enum MutualTailCall<A, B, R> {
1097 GoA(A, Box<dyn FnOnce(A) -> MutualTailCall<A, B, R>>),
1099 GoB(B, Box<dyn FnOnce(B) -> MutualTailCall<A, B, R>>),
1101 Done(R),
1103}
1104#[allow(dead_code)]
1106#[derive(Clone, Debug)]
1107pub enum ContinuationFrame {
1108 ApplyBinop { op: BinopKind, operand: u64 },
1110 StoreResult { var: String },
1112 PrintResult,
1114}
1115#[allow(dead_code)]
1117#[derive(Clone, Debug, PartialEq)]
1118pub enum PartialValue {
1119 Known(u64),
1121 Unknown(String),
1123 Bottom,
1125}
1126impl PartialValue {
1127 #[allow(dead_code)]
1129 pub fn is_known(&self) -> bool {
1130 matches!(self, PartialValue::Known(_))
1131 }
1132 #[allow(dead_code)]
1134 pub fn add(&self, other: &PartialValue) -> PartialValue {
1135 match (self, other) {
1136 (PartialValue::Known(a), PartialValue::Known(b)) => {
1137 PartialValue::Known(a.wrapping_add(*b))
1138 }
1139 (PartialValue::Bottom, _) | (_, PartialValue::Bottom) => PartialValue::Bottom,
1140 _ => PartialValue::Unknown(format!("{:?} + {:?}", self, other)),
1141 }
1142 }
1143 #[allow(dead_code)]
1145 pub fn mul(&self, other: &PartialValue) -> PartialValue {
1146 match (self, other) {
1147 (PartialValue::Known(a), PartialValue::Known(b)) => {
1148 PartialValue::Known(a.wrapping_mul(*b))
1149 }
1150 (PartialValue::Known(0), _) | (_, PartialValue::Known(0)) => PartialValue::Known(0),
1151 (PartialValue::Bottom, _) | (_, PartialValue::Bottom) => PartialValue::Bottom,
1152 _ => PartialValue::Unknown(format!("{:?} * {:?}", self, other)),
1153 }
1154 }
1155}
1156#[allow(dead_code)]
1158#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1159pub struct StateMachineState {
1160 pub id: u32,
1162 pub output: Vec<String>,
1164}
1165impl StateMachineState {
1166 #[allow(dead_code)]
1168 pub fn new(id: u32) -> Self {
1169 Self {
1170 id,
1171 output: Vec::new(),
1172 }
1173 }
1174 #[allow(dead_code)]
1176 pub fn emit(mut self, s: &str) -> Self {
1177 self.output.push(s.to_string());
1178 self
1179 }
1180}
1181#[allow(dead_code)]
1183pub struct ContinuationEvaluator {
1184 pub values: Vec<u64>,
1186 pub continuations: Vec<ContinuationFrame>,
1188 pub env: std::collections::HashMap<String, u64>,
1190}
1191impl ContinuationEvaluator {
1192 #[allow(dead_code)]
1194 pub fn new() -> Self {
1195 Self {
1196 values: Vec::new(),
1197 continuations: Vec::new(),
1198 env: std::collections::HashMap::new(),
1199 }
1200 }
1201 #[allow(dead_code)]
1203 pub fn push_value(&mut self, v: u64) {
1204 self.values.push(v);
1205 }
1206 #[allow(dead_code)]
1208 pub fn pop_value(&mut self) -> Option<u64> {
1209 self.values.pop()
1210 }
1211 #[allow(dead_code)]
1213 pub fn push_cont(&mut self, frame: ContinuationFrame) {
1214 self.continuations.push(frame);
1215 }
1216 #[allow(dead_code)]
1218 pub fn step(&mut self) -> bool {
1219 let frame = match self.continuations.pop() {
1220 Some(f) => f,
1221 None => return false,
1222 };
1223 match frame {
1224 ContinuationFrame::ApplyBinop { op, operand } => {
1225 if let Some(lhs) = self.values.pop() {
1226 if let Some(result) = op.eval(lhs, operand) {
1227 self.values.push(result);
1228 }
1229 }
1230 }
1231 ContinuationFrame::StoreResult { var } => {
1232 if let Some(v) = self.values.last().copied() {
1233 self.env.insert(var, v);
1234 }
1235 }
1236 ContinuationFrame::PrintResult => {}
1237 }
1238 true
1239 }
1240 #[allow(dead_code)]
1242 pub fn run(&mut self) {
1243 while self.step() {}
1244 }
1245}