Skip to main content

tensorlogic_compiler/inline/
config.rs

1/// Configuration for the let-inlining pass.
2///
3/// All flags default to `true` so that the pass is aggressive by default.
4/// Disable individual flags for conservative inlining or debugging.
5#[derive(Debug, Clone)]
6pub struct InlineConfig {
7    /// Inline `Let(x, e, body)` whenever `x` occurs free exactly once in
8    /// `body`, regardless of how complex `e` is (subject to `max_inline_depth`).
9    pub inline_single_use: bool,
10
11    /// Inline `Let(x, Constant(_), body)` regardless of how many times `x`
12    /// is used; constant duplication is essentially free.
13    pub inline_constants: bool,
14
15    /// Inline `Let(x, Pred(y, []), body)` — i.e. variable aliases — regardless
16    /// of use count; these are pure renames.
17    pub inline_vars: bool,
18
19    /// Maximum number of fixed-point passes before stopping.
20    pub max_passes: u32,
21
22    /// Do not inline a binding whose value expression has depth greater than
23    /// this threshold.  Prevents unbounded code-size growth.
24    pub max_inline_depth: usize,
25}
26
27impl Default for InlineConfig {
28    fn default() -> Self {
29        Self {
30            inline_single_use: true,
31            inline_constants: true,
32            inline_vars: true,
33            max_passes: 20,
34            max_inline_depth: 10,
35        }
36    }
37}
38
39/// Statistics collected by the let-inlining pass.
40#[derive(Debug, Clone, Default)]
41pub struct InlineStats {
42    /// Number of bindings inlined because the variable had exactly one free
43    /// occurrence in the body (and the value was not a constant or alias).
44    pub single_use_inlines: u64,
45
46    /// Number of bindings inlined because the value was a constant literal.
47    pub constant_inlines: u64,
48
49    /// Number of bindings inlined because the value was a variable alias
50    /// (zero-argument predicate).
51    pub variable_inlines: u64,
52
53    /// Total node count before the first pass.
54    pub nodes_before: u64,
55
56    /// Total node count after the final pass.
57    pub nodes_after: u64,
58
59    /// Number of passes executed.
60    pub passes: u32,
61}
62
63impl InlineStats {
64    /// Sum of all inlining categories.
65    pub fn total(&self) -> u64 {
66        self.single_use_inlines
67            .saturating_add(self.constant_inlines)
68            .saturating_add(self.variable_inlines)
69    }
70
71    /// Fraction of nodes removed: `(before − after) / before`.
72    ///
73    /// Returns `0.0` when `nodes_before == 0`.
74    pub fn reduction_pct(&self) -> f64 {
75        if self.nodes_before == 0 {
76            return 0.0;
77        }
78        let before = self.nodes_before as f64;
79        let after = self.nodes_after as f64;
80        ((before - after) / before * 100.0).max(0.0)
81    }
82
83    /// Human-readable one-line summary.
84    pub fn summary(&self) -> String {
85        format!(
86            "Inline: {} passes, {}/{} nodes kept ({:.1}% reduction) — \
87             {} single-use, {} constant, {} variable-alias inlines",
88            self.passes,
89            self.nodes_after,
90            self.nodes_before,
91            self.reduction_pct(),
92            self.single_use_inlines,
93            self.constant_inlines,
94            self.variable_inlines,
95        )
96    }
97}