Skip to main content

oxilean_codegen/cil_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::functions::*;
9use std::collections::{HashSet, VecDeque};
10
11/// A reference to a .NET field.
12#[derive(Debug, Clone)]
13pub struct CilFieldRef {
14    pub field_type: CilType,
15    pub declaring_type: CilType,
16    pub name: std::string::String,
17}
18/// CIL instruction set.
19///
20/// Covers the major opcodes from ECMA-335 Partition III.
21#[derive(Debug, Clone)]
22pub enum CilInstr {
23    /// `nop` — no operation
24    Nop,
25    /// `ldloc.s <idx>` — load local variable
26    LdlocS(u16),
27    /// `stloc.s <idx>` — store local variable
28    StlocS(u16),
29    /// `ldloca.s <idx>` — load address of local
30    LdlocaS(u16),
31    /// `ldarg.s <idx>` — load argument
32    LdargS(u16),
33    /// `starg.s <idx>` — store argument
34    StargS(u16),
35    /// `ldarga.s <idx>` — load argument address
36    LdargaS(u16),
37    /// `ldc.i4 <val>` — load int32 constant
38    LdcI4(i32),
39    /// `ldc.i4.s <val>` — load int32 constant (short form, -128..127)
40    LdcI4S(i8),
41    /// `ldc.i4.N` — load small int32 (N = 0..8)
42    LdcI4Small(i32),
43    /// `ldc.i8 <val>` — load int64 constant
44    LdcI8(i64),
45    /// `ldc.r4 <val>` — load float32 constant
46    LdcR4(f32),
47    /// `ldc.r8 <val>` — load float64 constant
48    LdcR8(f64),
49    /// `ldnull` — load null reference
50    Ldnull,
51    /// `ldstr <s>` — load string literal
52    Ldstr(std::string::String),
53    /// `ldsflda <field>` — load address of static field
54    Ldsflda(CilFieldRef),
55    /// `ldsfld <field>` — load static field value
56    Ldsfld(CilFieldRef),
57    /// `stsfld <field>` — store static field value
58    Stsfld(CilFieldRef),
59    /// `add`
60    Add,
61    /// `add.ovf` — checked addition
62    AddOvf,
63    /// `sub`
64    Sub,
65    /// `sub.ovf` — checked subtraction
66    SubOvf,
67    /// `mul`
68    Mul,
69    /// `mul.ovf` — checked multiplication
70    MulOvf,
71    /// `div`
72    Div,
73    /// `div.un` — unsigned division
74    DivUn,
75    /// `rem`
76    Rem,
77    /// `rem.un` — unsigned remainder
78    RemUn,
79    /// `neg` — negate
80    Neg,
81    /// `and`
82    And,
83    /// `or`
84    Or,
85    /// `xor`
86    Xor,
87    /// `not`
88    Not,
89    /// `shl` — shift left
90    Shl,
91    /// `shr` — shift right (signed)
92    Shr,
93    /// `shr.un` — shift right (unsigned)
94    ShrUn,
95    /// `ceq` — compare equal (push 1 or 0)
96    Ceq,
97    /// `cgt`
98    Cgt,
99    /// `cgt.un`
100    CgtUn,
101    /// `clt`
102    Clt,
103    /// `clt.un`
104    CltUn,
105    /// `br <label>`
106    Br(std::string::String),
107    /// `brfalse <label>`
108    Brfalse(std::string::String),
109    /// `brtrue <label>`
110    Brtrue(std::string::String),
111    /// `beq <label>`
112    Beq(std::string::String),
113    /// `bne.un <label>`
114    BneUn(std::string::String),
115    /// `blt <label>`
116    Blt(std::string::String),
117    /// `bgt <label>`
118    Bgt(std::string::String),
119    /// `ble <label>`
120    Ble(std::string::String),
121    /// `bge <label>`
122    Bge(std::string::String),
123    /// `switch <labels...>`
124    Switch(Vec<std::string::String>),
125    /// `ret`
126    Ret,
127    /// `throw`
128    Throw,
129    /// `rethrow`
130    Rethrow,
131    /// A label definition point
132    Label(std::string::String),
133    /// `call <method>`
134    Call(CilMethodRef),
135    /// `callvirt <method>`
136    Callvirt(CilMethodRef),
137    /// `tail. call <method>`
138    TailCall(CilMethodRef),
139    /// `calli <signature>`
140    Calli(CilCallSig),
141    /// `ldftn <method>`
142    Ldftn(CilMethodRef),
143    /// `ldvirtftn <method>`
144    Ldvirtftn(CilMethodRef),
145    /// `newobj <ctor>`
146    Newobj(CilMethodRef),
147    /// `ldobj <type>`
148    Ldobj(CilType),
149    /// `stobj <type>`
150    Stobj(CilType),
151    /// `ldfld <field>`
152    Ldfld(CilFieldRef),
153    /// `stfld <field>`
154    Stfld(CilFieldRef),
155    /// `ldflda <field>`
156    Ldflda(CilFieldRef),
157    /// `box <type>`
158    Box_(CilType),
159    /// `unbox <type>`
160    Unbox(CilType),
161    /// `unbox.any <type>`
162    UnboxAny(CilType),
163    /// `isinst <type>`
164    Isinst(CilType),
165    /// `castclass <type>`
166    Castclass(CilType),
167    /// `initobj <type>`
168    Initobj(CilType),
169    /// `sizeof <type>`
170    Sizeof(CilType),
171    /// `ldtoken <type>`
172    Ldtoken(CilType),
173    /// `newarr <type>`
174    Newarr(CilType),
175    /// `ldlen`
176    Ldlen,
177    /// `ldelem <type>`
178    Ldelem(CilType),
179    /// `stelem <type>`
180    Stelem(CilType),
181    /// `ldelema <type>`
182    Ldelema(CilType),
183    /// `dup`
184    Dup,
185    /// `pop`
186    Pop,
187    /// `conv.i4`
188    ConvI4,
189    /// `conv.i8`
190    ConvI8,
191    /// `conv.r4`
192    ConvR4,
193    /// `conv.r8`
194    ConvR8,
195    /// `conv.u4`
196    ConvU4,
197    /// `conv.u8`
198    ConvU8,
199    /// `ldind.i4`
200    LdindI4,
201    /// `stind.i4`
202    StindI4,
203    /// `localloc`
204    Localloc,
205    /// Comment (for IL dump readability)
206    Comment(std::string::String),
207}
208#[allow(dead_code)]
209pub struct CILPassRegistry {
210    pub(super) configs: Vec<CILPassConfig>,
211    pub(super) stats: std::collections::HashMap<String, CILPassStats>,
212}
213impl CILPassRegistry {
214    #[allow(dead_code)]
215    pub fn new() -> Self {
216        CILPassRegistry {
217            configs: Vec::new(),
218            stats: std::collections::HashMap::new(),
219        }
220    }
221    #[allow(dead_code)]
222    pub fn register(&mut self, config: CILPassConfig) {
223        self.stats
224            .insert(config.pass_name.clone(), CILPassStats::new());
225        self.configs.push(config);
226    }
227    #[allow(dead_code)]
228    pub fn enabled_passes(&self) -> Vec<&CILPassConfig> {
229        self.configs.iter().filter(|c| c.enabled).collect()
230    }
231    #[allow(dead_code)]
232    pub fn get_stats(&self, name: &str) -> Option<&CILPassStats> {
233        self.stats.get(name)
234    }
235    #[allow(dead_code)]
236    pub fn total_passes(&self) -> usize {
237        self.configs.len()
238    }
239    #[allow(dead_code)]
240    pub fn enabled_count(&self) -> usize {
241        self.enabled_passes().len()
242    }
243    #[allow(dead_code)]
244    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
245        if let Some(stats) = self.stats.get_mut(name) {
246            stats.record_run(changes, time_ms, iter);
247        }
248    }
249}
250/// Tracks declared names for CilExt scope analysis.
251#[derive(Debug, Default)]
252pub struct CilExtNameScope {
253    pub(super) declared: std::collections::HashSet<String>,
254    pub(super) depth: usize,
255    pub(super) parent: Option<Box<CilExtNameScope>>,
256}
257impl CilExtNameScope {
258    pub fn new() -> Self {
259        CilExtNameScope::default()
260    }
261    pub fn declare(&mut self, name: impl Into<String>) -> bool {
262        self.declared.insert(name.into())
263    }
264    pub fn is_declared(&self, name: &str) -> bool {
265        self.declared.contains(name)
266    }
267    pub fn push_scope(self) -> Self {
268        CilExtNameScope {
269            declared: std::collections::HashSet::new(),
270            depth: self.depth + 1,
271            parent: Some(Box::new(self)),
272        }
273    }
274    pub fn pop_scope(self) -> Self {
275        *self.parent.unwrap_or_default()
276    }
277    pub fn depth(&self) -> usize {
278        self.depth
279    }
280    pub fn len(&self) -> usize {
281        self.declared.len()
282    }
283}
284/// A fixed-capacity ring buffer of strings (for recent-event logging in CilExt).
285#[derive(Debug)]
286pub struct CilExtEventLog {
287    pub(super) entries: std::collections::VecDeque<String>,
288    pub(super) capacity: usize,
289}
290impl CilExtEventLog {
291    pub fn new(capacity: usize) -> Self {
292        CilExtEventLog {
293            entries: std::collections::VecDeque::with_capacity(capacity),
294            capacity,
295        }
296    }
297    pub fn push(&mut self, event: impl Into<String>) {
298        if self.entries.len() >= self.capacity {
299            self.entries.pop_front();
300        }
301        self.entries.push_back(event.into());
302    }
303    pub fn iter(&self) -> impl Iterator<Item = &String> {
304        self.entries.iter()
305    }
306    pub fn len(&self) -> usize {
307        self.entries.len()
308    }
309    pub fn is_empty(&self) -> bool {
310        self.entries.is_empty()
311    }
312    pub fn capacity(&self) -> usize {
313        self.capacity
314    }
315    pub fn clear(&mut self) {
316        self.entries.clear();
317    }
318}
319/// A monotonically increasing ID generator for CilExt.
320#[derive(Debug, Default)]
321pub struct CilExtIdGen {
322    pub(super) next: u32,
323}
324impl CilExtIdGen {
325    pub fn new() -> Self {
326        CilExtIdGen::default()
327    }
328    pub fn next_id(&mut self) -> u32 {
329        let id = self.next;
330        self.next += 1;
331        id
332    }
333    pub fn peek_next(&self) -> u32 {
334        self.next
335    }
336    pub fn reset(&mut self) {
337        self.next = 0;
338    }
339    pub fn skip(&mut self, n: u32) {
340        self.next += n;
341    }
342}
343#[allow(dead_code)]
344#[derive(Debug, Clone, PartialEq)]
345pub enum CILPassPhase {
346    Analysis,
347    Transformation,
348    Verification,
349    Cleanup,
350}
351impl CILPassPhase {
352    #[allow(dead_code)]
353    pub fn name(&self) -> &str {
354        match self {
355            CILPassPhase::Analysis => "analysis",
356            CILPassPhase::Transformation => "transformation",
357            CILPassPhase::Verification => "verification",
358            CILPassPhase::Cleanup => "cleanup",
359        }
360    }
361    #[allow(dead_code)]
362    pub fn is_modifying(&self) -> bool {
363        matches!(self, CILPassPhase::Transformation | CILPassPhase::Cleanup)
364    }
365}
366#[allow(dead_code)]
367#[derive(Debug, Clone)]
368pub struct CILDepGraph {
369    pub(super) nodes: Vec<u32>,
370    pub(super) edges: Vec<(u32, u32)>,
371}
372impl CILDepGraph {
373    #[allow(dead_code)]
374    pub fn new() -> Self {
375        CILDepGraph {
376            nodes: Vec::new(),
377            edges: Vec::new(),
378        }
379    }
380    #[allow(dead_code)]
381    pub fn add_node(&mut self, id: u32) {
382        if !self.nodes.contains(&id) {
383            self.nodes.push(id);
384        }
385    }
386    #[allow(dead_code)]
387    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
388        self.add_node(dep);
389        self.add_node(dependent);
390        self.edges.push((dep, dependent));
391    }
392    #[allow(dead_code)]
393    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
394        self.edges
395            .iter()
396            .filter(|(d, _)| *d == node)
397            .map(|(_, dep)| *dep)
398            .collect()
399    }
400    #[allow(dead_code)]
401    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
402        self.edges
403            .iter()
404            .filter(|(_, dep)| *dep == node)
405            .map(|(d, _)| *d)
406            .collect()
407    }
408    #[allow(dead_code)]
409    pub fn topological_sort(&self) -> Vec<u32> {
410        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
411        for &n in &self.nodes {
412            in_degree.insert(n, 0);
413        }
414        for (_, dep) in &self.edges {
415            *in_degree.entry(*dep).or_insert(0) += 1;
416        }
417        let mut queue: std::collections::VecDeque<u32> = self
418            .nodes
419            .iter()
420            .filter(|&&n| in_degree[&n] == 0)
421            .copied()
422            .collect();
423        let mut result = Vec::new();
424        while let Some(node) = queue.pop_front() {
425            result.push(node);
426            for dep in self.dependents_of(node) {
427                let cnt = in_degree.entry(dep).or_insert(0);
428                *cnt = cnt.saturating_sub(1);
429                if *cnt == 0 {
430                    queue.push_back(dep);
431                }
432            }
433        }
434        result
435    }
436    #[allow(dead_code)]
437    pub fn has_cycle(&self) -> bool {
438        self.topological_sort().len() < self.nodes.len()
439    }
440}
441/// A compile-time literal value (used in field initializers).
442#[derive(Debug, Clone)]
443pub enum CilLiteral {
444    Bool(bool),
445    Int32(i32),
446    Int64(i64),
447    Float32(f32),
448    Float64(f64),
449    String(std::string::String),
450    Null,
451}
452/// CIL code generation backend.
453///
454/// Converts LCNF IR into .NET CIL IL assembly (suitable for `ilasm`).
455pub struct CilBackend {
456    pub assembly: CilAssembly,
457    pub(super) label_counter: u32,
458    pub(super) var_locals: HashMap<u64, u16>,
459    pub default_namespace: std::string::String,
460}
461impl CilBackend {
462    /// Create a new CIL backend.
463    pub fn new(assembly_name: impl Into<std::string::String>) -> Self {
464        CilBackend {
465            assembly: CilAssembly::new(assembly_name),
466            label_counter: 0,
467            var_locals: HashMap::new(),
468            default_namespace: "OxiLean.Generated".to_string(),
469        }
470    }
471    /// Generate a fresh IL label.
472    pub(super) fn fresh_label(&mut self) -> std::string::String {
473        let n = self.label_counter;
474        self.label_counter += 1;
475        format!("IL_{:04X}", n)
476    }
477    /// Map an LCNF type to a CIL type.
478    pub fn lcnf_to_cil_type(&self, ty: &LcnfType) -> CilType {
479        match ty {
480            LcnfType::Erased | LcnfType::Object | LcnfType::Irrelevant => CilType::Object,
481            LcnfType::Nat => CilType::UInt64,
482            LcnfType::LcnfString => CilType::String,
483            LcnfType::Unit => CilType::Void,
484            LcnfType::Var(name) => match name.as_str() {
485                "Int32" | "int32" => CilType::Int32,
486                "Int64" | "int64" | "Int" => CilType::Int64,
487                "UInt32" | "uint32" => CilType::UInt32,
488                "Float" | "Float32" | "float32" => CilType::Float32,
489                "Float64" | "float64" | "Double" => CilType::Float64,
490                "Bool" | "bool" => CilType::Bool,
491                "String" | "string" => CilType::String,
492                "Unit" | "void" => CilType::Void,
493                "Char" | "char" => CilType::Char,
494                _ => CilType::class_in(&self.default_namespace, name.clone()),
495            },
496            LcnfType::Fun(params, ret) => {
497                if params.len() == 1 {
498                    CilType::func_of(
499                        self.lcnf_to_cil_type(&params[0]),
500                        self.lcnf_to_cil_type(ret),
501                    )
502                } else {
503                    CilType::class_in("System", "Delegate")
504                }
505            }
506            LcnfType::Ctor(name, _) => CilType::class_in(&self.default_namespace, name.clone()),
507        }
508    }
509    /// Emit CIL instructions for an LCNF literal.
510    pub fn emit_literal(&self, method: &mut CilMethod, lit: &LcnfLit) {
511        match lit {
512            LcnfLit::Nat(n) => method.emit(CilInstr::LdcI8(*n as i64)),
513            LcnfLit::Str(s) => method.emit(CilInstr::Ldstr(s.clone())),
514        }
515    }
516    /// Emit CIL instructions for an LCNF argument.
517    pub fn emit_arg(&mut self, method: &mut CilMethod, arg: &LcnfArg) {
518        match arg {
519            LcnfArg::Var(id) => {
520                if let Some(&local_idx) = self.var_locals.get(&id.0) {
521                    method.emit(CilInstr::LdlocS(local_idx));
522                } else {
523                    method.emit(CilInstr::LdargS(0));
524                }
525            }
526            LcnfArg::Lit(lit) => self.emit_literal(method, lit),
527            LcnfArg::Erased | LcnfArg::Type(_) => method.emit(CilInstr::Ldnull),
528        }
529    }
530    /// Emit CIL instructions for an LCNF let-value.
531    pub fn emit_let_value(&mut self, method: &mut CilMethod, val: &LcnfLetValue) {
532        match val {
533            LcnfLetValue::App(func, args) => {
534                for arg in args.iter() {
535                    self.emit_arg(method, arg);
536                }
537                self.emit_arg(method, func);
538                let invoke_ref = CilMethodRef {
539                    call_conv: CilCallConv::Instance,
540                    return_type: CilType::Object,
541                    declaring_type: CilType::class_in("System", "Delegate"),
542                    name: "DynamicInvoke".to_string(),
543                    param_types: vec![CilType::Array(Box::new(CilType::Object))],
544                };
545                method.emit(CilInstr::Callvirt(invoke_ref));
546            }
547            LcnfLetValue::Proj(_struct_name, idx, var_id) => {
548                if let Some(&local_idx) = self.var_locals.get(&var_id.0) {
549                    method.emit(CilInstr::LdlocS(local_idx));
550                } else {
551                    method.emit(CilInstr::LdargS(0));
552                }
553                let field_ref = CilFieldRef {
554                    field_type: CilType::Object,
555                    declaring_type: CilType::Object,
556                    name: format!("_field{}", idx),
557                };
558                method.emit(CilInstr::Ldfld(field_ref));
559            }
560            LcnfLetValue::Ctor(name, _tag, args) => {
561                let ctor_type = CilType::class_in(self.default_namespace.clone(), name.clone());
562                let param_types: Vec<CilType> = args.iter().map(|_| CilType::Object).collect();
563                for arg in args.iter() {
564                    self.emit_arg(method, arg);
565                }
566                method.emit(CilInstr::Newobj(CilMethodRef {
567                    call_conv: CilCallConv::Instance,
568                    return_type: CilType::Void,
569                    declaring_type: ctor_type,
570                    name: ".ctor".to_string(),
571                    param_types,
572                }));
573            }
574            LcnfLetValue::Lit(lit) => self.emit_literal(method, lit),
575            LcnfLetValue::Erased => method.emit(CilInstr::Ldnull),
576            LcnfLetValue::FVar(id) => {
577                if let Some(&local_idx) = self.var_locals.get(&id.0) {
578                    method.emit(CilInstr::LdlocS(local_idx));
579                } else {
580                    method.emit(CilInstr::LdargS(0));
581                }
582            }
583            LcnfLetValue::Reset(var) => {
584                if let Some(&local_idx) = self.var_locals.get(&var.0) {
585                    method.emit(CilInstr::LdlocS(local_idx));
586                } else {
587                    method.emit(CilInstr::LdargS(0));
588                }
589                method.emit(CilInstr::Comment("reset (reuse optimization)".to_string()));
590            }
591            LcnfLetValue::Reuse(slot, name, _tag, args) => {
592                let ctor_type = CilType::class_in(self.default_namespace.clone(), name.clone());
593                let param_types: Vec<CilType> = args.iter().map(|_| CilType::Object).collect();
594                if let Some(&local_idx) = self.var_locals.get(&slot.0) {
595                    method.emit(CilInstr::LdlocS(local_idx));
596                } else {
597                    method.emit(CilInstr::LdargS(0));
598                }
599                for arg in args.iter() {
600                    self.emit_arg(method, arg);
601                }
602                method.emit(CilInstr::Comment(format!("reuse -> {}", name)));
603                method.emit(CilInstr::Newobj(CilMethodRef {
604                    call_conv: CilCallConv::Instance,
605                    return_type: CilType::Void,
606                    declaring_type: ctor_type,
607                    name: ".ctor".to_string(),
608                    param_types,
609                }));
610            }
611        }
612    }
613    /// Emit CIL instructions for an LCNF expression.
614    #[allow(clippy::too_many_arguments)]
615    pub fn emit_expr(&mut self, method: &mut CilMethod, expr: &LcnfExpr) {
616        match expr {
617            LcnfExpr::Let {
618                id,
619                ty,
620                value,
621                body,
622                ..
623            } => {
624                self.emit_let_value(method, value);
625                let cil_ty = self.lcnf_to_cil_type(ty);
626                let local_idx = method.add_local(cil_ty, None);
627                self.var_locals.insert(id.0, local_idx);
628                method.emit(CilInstr::StlocS(local_idx));
629                self.emit_expr(method, body);
630            }
631            LcnfExpr::Case {
632                scrutinee,
633                alts,
634                default,
635                ..
636            } => {
637                let end_label = self.fresh_label();
638                for alt in alts.iter() {
639                    let next_label = self.fresh_label();
640                    if let Some(&local_idx) = self.var_locals.get(&scrutinee.0) {
641                        method.emit(CilInstr::LdlocS(local_idx));
642                    } else {
643                        method.emit(CilInstr::LdargS(0));
644                    }
645                    let ctor_type =
646                        CilType::class_in(self.default_namespace.clone(), alt.ctor_name.clone());
647                    method.emit(CilInstr::Isinst(ctor_type.clone()));
648                    method.emit(CilInstr::Dup);
649                    method.emit(CilInstr::Brfalse(next_label.clone()));
650                    for (i, param) in alt.params.iter().enumerate() {
651                        method.emit(CilInstr::Dup);
652                        let field_ref = CilFieldRef {
653                            field_type: CilType::Object,
654                            declaring_type: ctor_type.clone(),
655                            name: format!("_field{}", i),
656                        };
657                        method.emit(CilInstr::Ldfld(field_ref));
658                        let param_ty = self.lcnf_to_cil_type(&param.ty);
659                        let local_idx = method.add_local(param_ty, Some(param.name.clone()));
660                        self.var_locals.insert(param.id.0, local_idx);
661                        method.emit(CilInstr::StlocS(local_idx));
662                    }
663                    method.emit(CilInstr::Pop);
664                    let body = alt.body.clone();
665                    self.emit_expr(method, &body);
666                    method.emit(CilInstr::Br(end_label.clone()));
667                    method.emit_label(next_label);
668                    method.emit(CilInstr::Pop);
669                }
670                if let Some(def_body) = default {
671                    let def_body = def_body.clone();
672                    self.emit_expr(method, &def_body);
673                } else {
674                    method.emit(CilInstr::Ldstr("MatchFailure".to_string()));
675                    method.emit(CilInstr::Newobj(CilMethodRef {
676                        call_conv: CilCallConv::Instance,
677                        return_type: CilType::Void,
678                        declaring_type: CilType::class_in("System", "Exception"),
679                        name: ".ctor".to_string(),
680                        param_types: vec![CilType::String],
681                    }));
682                    method.emit(CilInstr::Throw);
683                }
684                method.emit_label(end_label);
685            }
686            LcnfExpr::Return(arg) => {
687                self.emit_arg(method, arg);
688            }
689            LcnfExpr::Unreachable => {
690                method.emit(CilInstr::Ldstr("unreachable".to_string()));
691                method.emit(CilInstr::Newobj(CilMethodRef {
692                    call_conv: CilCallConv::Instance,
693                    return_type: CilType::Void,
694                    declaring_type: CilType::class_in("System", "InvalidOperationException"),
695                    name: ".ctor".to_string(),
696                    param_types: vec![CilType::String],
697                }));
698                method.emit(CilInstr::Throw);
699            }
700            LcnfExpr::TailCall(func, args) => {
701                for arg in args.iter() {
702                    self.emit_arg(method, arg);
703                }
704                self.emit_arg(method, func);
705                let invoke_ref = CilMethodRef {
706                    call_conv: CilCallConv::Instance,
707                    return_type: CilType::Object,
708                    declaring_type: CilType::class_in("System", "Delegate"),
709                    name: "DynamicInvoke".to_string(),
710                    param_types: vec![CilType::Array(Box::new(CilType::Object))],
711                };
712                method.emit(CilInstr::TailCall(invoke_ref));
713            }
714        }
715    }
716    /// Emit a complete LCNF function declaration as a CIL method.
717    pub fn emit_fun_decl(&mut self, decl: &LcnfFunDecl) -> CilMethod {
718        let ret_ty = self.lcnf_to_cil_type(&decl.ret_type);
719        let mut method = CilMethod::new_static(&decl.name, ret_ty);
720        for param in &decl.params {
721            let cil_ty = self.lcnf_to_cil_type(&param.ty);
722            let idx = method.add_param(param.name.clone(), cil_ty);
723            self.var_locals.insert(param.id.0, idx);
724        }
725        let body = decl.body.clone();
726        self.emit_expr(&mut method, &body);
727        method.emit(CilInstr::Ret);
728        method
729    }
730    /// Emit the assembly as IL assembly source text (for `ilasm`).
731    pub fn emit_ilasm(&self) -> std::string::String {
732        let mut out = std::string::String::new();
733        out.push_str(".assembly extern mscorlib {}\n");
734        out.push_str(&format!(".assembly '{}'\n{{\n", self.assembly.name));
735        let (maj, min, bld, rev) = self.assembly.version;
736        out.push_str(&format!("  .ver {}:{}:{}:{}\n", maj, min, bld, rev));
737        out.push_str("}\n\n");
738        out.push_str(&format!(".module '{}.exe'\n\n", self.assembly.name));
739        for class in &self.assembly.classes {
740            out.push_str(&self.emit_class_ilasm(class));
741            out.push('\n');
742        }
743        out
744    }
745    /// Emit a single class in IL assembly syntax.
746    pub(super) fn emit_class_ilasm(&self, class: &CilClass) -> std::string::String {
747        let mut out = std::string::String::new();
748        let vis = class.visibility.to_string();
749        let kind = if class.is_interface {
750            "interface"
751        } else if class.is_value_type {
752            "value class"
753        } else {
754            "class"
755        };
756        let sealed = if class.is_sealed { " sealed" } else { "" };
757        let abst = if class.is_abstract { " abstract" } else { "" };
758        out.push_str(&format!(
759            ".class {} {}{}{} {}\n{{\n",
760            vis,
761            kind,
762            sealed,
763            abst,
764            class.full_name()
765        ));
766        if let Some(base) = &class.base_type {
767            out.push_str(&format!("  extends {}\n", base));
768        }
769        for field in &class.fields {
770            let static_kw = if field.is_static { "static " } else { "" };
771            out.push_str(&format!(
772                "  .field {} {}{} '{}'\n",
773                field.visibility, static_kw, field.ty, field.name
774            ));
775        }
776        for method in &class.methods {
777            out.push_str(&self.emit_method_ilasm(method));
778        }
779        out.push_str("} // end of class\n");
780        out
781    }
782    /// Emit a single method in IL assembly syntax.
783    pub(super) fn emit_method_ilasm(&self, method: &CilMethod) -> std::string::String {
784        let mut out = std::string::String::new();
785        let static_kw = if method.is_static {
786            "static "
787        } else {
788            "instance "
789        };
790        let virtual_kw = if method.is_virtual { "virtual " } else { "" };
791        let vis = method.visibility.to_string();
792        let params_str = method
793            .params
794            .iter()
795            .map(|(name, ty)| format!("{} '{}'", ty, name))
796            .collect::<Vec<_>>()
797            .join(", ");
798        out.push_str(&format!(
799            "  .method {} {}{}{} '{}'({}) cil managed\n  {{\n",
800            vis, static_kw, virtual_kw, method.return_type, method.name, params_str
801        ));
802        if let Some((_, ref ep_method)) = self.assembly.entry_point {
803            if ep_method == &method.name {
804                out.push_str("    .entrypoint\n");
805            }
806        }
807        out.push_str(&format!("    .maxstack {}\n", method.max_stack));
808        if !method.locals.is_empty() {
809            out.push_str("    .locals init (");
810            for (i, local) in method.locals.iter().enumerate() {
811                if i > 0 {
812                    out.push_str(", ");
813                }
814                let name_str = local
815                    .name
816                    .as_deref()
817                    .map(|n| format!(" '{}'", n))
818                    .unwrap_or_default();
819                out.push_str(&format!("[{}] {}{}", i, local.ty, name_str));
820            }
821            out.push_str(")\n");
822        }
823        for instr in &method.instructions {
824            match instr {
825                CilInstr::Label(lbl) => out.push_str(&format!("  {}:\n", lbl)),
826                CilInstr::Comment(s) => out.push_str(&format!("    // {}\n", s)),
827                _ => out.push_str(&format!("    {}\n", emit_cil_instr(instr))),
828            }
829        }
830        out.push_str("  } // end of method\n");
831        out
832    }
833}
834#[allow(dead_code)]
835pub struct CILConstantFoldingHelper;
836impl CILConstantFoldingHelper {
837    #[allow(dead_code)]
838    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
839        a.checked_add(b)
840    }
841    #[allow(dead_code)]
842    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
843        a.checked_sub(b)
844    }
845    #[allow(dead_code)]
846    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
847        a.checked_mul(b)
848    }
849    #[allow(dead_code)]
850    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
851        if b == 0 {
852            None
853        } else {
854            a.checked_div(b)
855        }
856    }
857    #[allow(dead_code)]
858    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
859        a + b
860    }
861    #[allow(dead_code)]
862    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
863        a * b
864    }
865    #[allow(dead_code)]
866    pub fn fold_neg_i64(a: i64) -> Option<i64> {
867        a.checked_neg()
868    }
869    #[allow(dead_code)]
870    pub fn fold_not_bool(a: bool) -> bool {
871        !a
872    }
873    #[allow(dead_code)]
874    pub fn fold_and_bool(a: bool, b: bool) -> bool {
875        a && b
876    }
877    #[allow(dead_code)]
878    pub fn fold_or_bool(a: bool, b: bool) -> bool {
879        a || b
880    }
881    #[allow(dead_code)]
882    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
883        a.checked_shl(b)
884    }
885    #[allow(dead_code)]
886    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
887        a.checked_shr(b)
888    }
889    #[allow(dead_code)]
890    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
891        if b == 0 {
892            None
893        } else {
894            Some(a % b)
895        }
896    }
897    #[allow(dead_code)]
898    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
899        a & b
900    }
901    #[allow(dead_code)]
902    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
903        a | b
904    }
905    #[allow(dead_code)]
906    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
907        a ^ b
908    }
909    #[allow(dead_code)]
910    pub fn fold_bitnot_i64(a: i64) -> i64 {
911        !a
912    }
913}
914#[allow(dead_code)]
915#[derive(Debug, Clone)]
916pub struct CILCacheEntry {
917    pub key: String,
918    pub data: Vec<u8>,
919    pub timestamp: u64,
920    pub valid: bool,
921}
922#[allow(dead_code)]
923#[derive(Debug, Clone)]
924pub struct CILLivenessInfo {
925    pub live_in: Vec<std::collections::HashSet<u32>>,
926    pub live_out: Vec<std::collections::HashSet<u32>>,
927    pub defs: Vec<std::collections::HashSet<u32>>,
928    pub uses: Vec<std::collections::HashSet<u32>>,
929}
930impl CILLivenessInfo {
931    #[allow(dead_code)]
932    pub fn new(block_count: usize) -> Self {
933        CILLivenessInfo {
934            live_in: vec![std::collections::HashSet::new(); block_count],
935            live_out: vec![std::collections::HashSet::new(); block_count],
936            defs: vec![std::collections::HashSet::new(); block_count],
937            uses: vec![std::collections::HashSet::new(); block_count],
938        }
939    }
940    #[allow(dead_code)]
941    pub fn add_def(&mut self, block: usize, var: u32) {
942        if block < self.defs.len() {
943            self.defs[block].insert(var);
944        }
945    }
946    #[allow(dead_code)]
947    pub fn add_use(&mut self, block: usize, var: u32) {
948        if block < self.uses.len() {
949            self.uses[block].insert(var);
950        }
951    }
952    #[allow(dead_code)]
953    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
954        self.live_in
955            .get(block)
956            .map(|s| s.contains(&var))
957            .unwrap_or(false)
958    }
959    #[allow(dead_code)]
960    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
961        self.live_out
962            .get(block)
963            .map(|s| s.contains(&var))
964            .unwrap_or(false)
965    }
966}
967/// A feature flag set for CilExt capabilities.
968#[derive(Debug, Clone, Default)]
969pub struct CilExtFeatures {
970    pub(super) flags: std::collections::HashSet<String>,
971}
972impl CilExtFeatures {
973    pub fn new() -> Self {
974        CilExtFeatures::default()
975    }
976    pub fn enable(&mut self, flag: impl Into<String>) {
977        self.flags.insert(flag.into());
978    }
979    pub fn disable(&mut self, flag: &str) {
980        self.flags.remove(flag);
981    }
982    pub fn is_enabled(&self, flag: &str) -> bool {
983        self.flags.contains(flag)
984    }
985    pub fn len(&self) -> usize {
986        self.flags.len()
987    }
988    pub fn is_empty(&self) -> bool {
989        self.flags.is_empty()
990    }
991    pub fn union(&self, other: &CilExtFeatures) -> CilExtFeatures {
992        CilExtFeatures {
993            flags: self.flags.union(&other.flags).cloned().collect(),
994        }
995    }
996    pub fn intersection(&self, other: &CilExtFeatures) -> CilExtFeatures {
997        CilExtFeatures {
998            flags: self.flags.intersection(&other.flags).cloned().collect(),
999        }
1000    }
1001}
1002/// A local variable declaration within a CIL method.
1003#[derive(Debug, Clone)]
1004pub struct CilLocal {
1005    pub index: u16,
1006    pub ty: CilType,
1007    pub name: Option<std::string::String>,
1008}
1009#[allow(dead_code)]
1010#[derive(Debug, Clone)]
1011pub struct CILPassConfig {
1012    pub phase: CILPassPhase,
1013    pub enabled: bool,
1014    pub max_iterations: u32,
1015    pub debug_output: bool,
1016    pub pass_name: String,
1017}
1018impl CILPassConfig {
1019    #[allow(dead_code)]
1020    pub fn new(name: impl Into<String>, phase: CILPassPhase) -> Self {
1021        CILPassConfig {
1022            phase,
1023            enabled: true,
1024            max_iterations: 10,
1025            debug_output: false,
1026            pass_name: name.into(),
1027        }
1028    }
1029    #[allow(dead_code)]
1030    pub fn disabled(mut self) -> Self {
1031        self.enabled = false;
1032        self
1033    }
1034    #[allow(dead_code)]
1035    pub fn with_debug(mut self) -> Self {
1036        self.debug_output = true;
1037        self
1038    }
1039    #[allow(dead_code)]
1040    pub fn max_iter(mut self, n: u32) -> Self {
1041        self.max_iterations = n;
1042        self
1043    }
1044}
1045#[allow(dead_code)]
1046#[derive(Debug, Clone)]
1047pub struct CILAnalysisCache {
1048    pub(super) entries: std::collections::HashMap<String, CILCacheEntry>,
1049    pub(super) max_size: usize,
1050    pub(super) hits: u64,
1051    pub(super) misses: u64,
1052}
1053impl CILAnalysisCache {
1054    #[allow(dead_code)]
1055    pub fn new(max_size: usize) -> Self {
1056        CILAnalysisCache {
1057            entries: std::collections::HashMap::new(),
1058            max_size,
1059            hits: 0,
1060            misses: 0,
1061        }
1062    }
1063    #[allow(dead_code)]
1064    pub fn get(&mut self, key: &str) -> Option<&CILCacheEntry> {
1065        if self.entries.contains_key(key) {
1066            self.hits += 1;
1067            self.entries.get(key)
1068        } else {
1069            self.misses += 1;
1070            None
1071        }
1072    }
1073    #[allow(dead_code)]
1074    pub fn insert(&mut self, key: String, data: Vec<u8>) {
1075        if self.entries.len() >= self.max_size {
1076            if let Some(oldest) = self.entries.keys().next().cloned() {
1077                self.entries.remove(&oldest);
1078            }
1079        }
1080        self.entries.insert(
1081            key.clone(),
1082            CILCacheEntry {
1083                key,
1084                data,
1085                timestamp: 0,
1086                valid: true,
1087            },
1088        );
1089    }
1090    #[allow(dead_code)]
1091    pub fn invalidate(&mut self, key: &str) {
1092        if let Some(entry) = self.entries.get_mut(key) {
1093            entry.valid = false;
1094        }
1095    }
1096    #[allow(dead_code)]
1097    pub fn clear(&mut self) {
1098        self.entries.clear();
1099    }
1100    #[allow(dead_code)]
1101    pub fn hit_rate(&self) -> f64 {
1102        let total = self.hits + self.misses;
1103        if total == 0 {
1104            return 0.0;
1105        }
1106        self.hits as f64 / total as f64
1107    }
1108    #[allow(dead_code)]
1109    pub fn size(&self) -> usize {
1110        self.entries.len()
1111    }
1112}
1113/// CIL type representation.
1114///
1115/// Covers all types available in the .NET Common Type System (CTS).
1116#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1117pub enum CilType {
1118    /// `void` — no return value
1119    Void,
1120    /// `bool` — System.Boolean (1 byte)
1121    Bool,
1122    /// `int8` — System.SByte
1123    Int8,
1124    /// `int16` — System.Int16
1125    Int16,
1126    /// `int32` — System.Int32
1127    Int32,
1128    /// `int64` — System.Int64
1129    Int64,
1130    /// `uint8` — System.Byte
1131    UInt8,
1132    /// `uint16` — System.UInt16
1133    UInt16,
1134    /// `uint32` — System.UInt32
1135    UInt32,
1136    /// `uint64` — System.UInt64
1137    UInt64,
1138    /// `float32` — System.Single
1139    Float32,
1140    /// `float64` — System.Double
1141    Float64,
1142    /// `char` — System.Char (UTF-16 code unit)
1143    Char,
1144    /// `string` — System.String (immutable UTF-16 string)
1145    String,
1146    /// `object` — System.Object (root of the type hierarchy)
1147    Object,
1148    /// `class [Assembly]Namespace.TypeName`
1149    Class {
1150        assembly: Option<std::string::String>,
1151        namespace: std::string::String,
1152        name: std::string::String,
1153    },
1154    /// `valuetype [Assembly]Namespace.TypeName`
1155    ValueType {
1156        assembly: Option<std::string::String>,
1157        namespace: std::string::String,
1158        name: std::string::String,
1159    },
1160    /// `T[]` — single-dimensional array (szarray)
1161    Array(Box<CilType>),
1162    /// `T[,]` — multi-dimensional array
1163    MdArray(Box<CilType>, u32),
1164    /// `T*` — unmanaged pointer
1165    Ptr(Box<CilType>),
1166    /// `T&` — managed reference (byref)
1167    ByRef(Box<CilType>),
1168    /// Generic instance: `class MyType<T1, T2>`
1169    Generic(Box<CilType>, Vec<CilType>),
1170    /// Generic parameter `!T` (class-level)
1171    GenericParam(u32),
1172    /// Generic method parameter `!!T`
1173    GenericMethodParam(u32),
1174    /// `native int` — platform-native integer
1175    NativeInt,
1176    /// `native uint`
1177    NativeUInt,
1178}
1179impl CilType {
1180    /// Returns `true` if this is a value type (not a reference type).
1181    pub fn is_value_type(&self) -> bool {
1182        matches!(
1183            self,
1184            CilType::Bool
1185                | CilType::Int8
1186                | CilType::Int16
1187                | CilType::Int32
1188                | CilType::Int64
1189                | CilType::UInt8
1190                | CilType::UInt16
1191                | CilType::UInt32
1192                | CilType::UInt64
1193                | CilType::Float32
1194                | CilType::Float64
1195                | CilType::Char
1196                | CilType::NativeInt
1197                | CilType::NativeUInt
1198                | CilType::ValueType { .. }
1199        )
1200    }
1201    /// Returns `true` if this is a reference type.
1202    pub fn is_reference_type(&self) -> bool {
1203        !self.is_value_type()
1204    }
1205    /// Return the fully-qualified boxed name for use in generics.
1206    pub fn boxed_name(&self) -> &'static str {
1207        match self {
1208            CilType::Bool => "System.Boolean",
1209            CilType::Int8 => "System.SByte",
1210            CilType::Int16 => "System.Int16",
1211            CilType::Int32 => "System.Int32",
1212            CilType::Int64 => "System.Int64",
1213            CilType::Float32 => "System.Single",
1214            CilType::Float64 => "System.Double",
1215            CilType::Char => "System.Char",
1216            _ => "System.Object",
1217        }
1218    }
1219    /// Helper: create a `class` type in the given namespace.
1220    pub fn class_in(
1221        namespace: impl Into<std::string::String>,
1222        name: impl Into<std::string::String>,
1223    ) -> Self {
1224        CilType::Class {
1225            assembly: None,
1226            namespace: namespace.into(),
1227            name: name.into(),
1228        }
1229    }
1230    /// Helper: `System.Collections.Generic.List<T>`.
1231    pub fn list_of(elem: CilType) -> Self {
1232        CilType::Generic(
1233            Box::new(CilType::class_in("System.Collections.Generic", "List`1")),
1234            vec![elem],
1235        )
1236    }
1237    /// Helper: `System.Func<TArg, TResult>`.
1238    pub fn func_of(arg: CilType, result: CilType) -> Self {
1239        CilType::Generic(
1240            Box::new(CilType::class_in("System", "Func`2")),
1241            vec![arg, result],
1242        )
1243    }
1244}
1245/// A text buffer for building CilExt output source code.
1246#[derive(Debug, Default)]
1247pub struct CilExtSourceBuffer {
1248    pub(super) buf: String,
1249    pub(super) indent_level: usize,
1250    pub(super) indent_str: String,
1251}
1252impl CilExtSourceBuffer {
1253    pub fn new() -> Self {
1254        CilExtSourceBuffer {
1255            buf: String::new(),
1256            indent_level: 0,
1257            indent_str: "    ".to_string(),
1258        }
1259    }
1260    pub fn with_indent(mut self, indent: impl Into<String>) -> Self {
1261        self.indent_str = indent.into();
1262        self
1263    }
1264    pub fn push_line(&mut self, line: &str) {
1265        for _ in 0..self.indent_level {
1266            self.buf.push_str(&self.indent_str);
1267        }
1268        self.buf.push_str(line);
1269        self.buf.push('\n');
1270    }
1271    pub fn push_raw(&mut self, s: &str) {
1272        self.buf.push_str(s);
1273    }
1274    pub fn indent(&mut self) {
1275        self.indent_level += 1;
1276    }
1277    pub fn dedent(&mut self) {
1278        self.indent_level = self.indent_level.saturating_sub(1);
1279    }
1280    pub fn as_str(&self) -> &str {
1281        &self.buf
1282    }
1283    pub fn len(&self) -> usize {
1284        self.buf.len()
1285    }
1286    pub fn is_empty(&self) -> bool {
1287        self.buf.is_empty()
1288    }
1289    pub fn line_count(&self) -> usize {
1290        self.buf.lines().count()
1291    }
1292    pub fn into_string(self) -> String {
1293        self.buf
1294    }
1295    pub fn reset(&mut self) {
1296        self.buf.clear();
1297        self.indent_level = 0;
1298    }
1299}
1300/// Pipeline profiler for CilExt.
1301#[derive(Debug, Default)]
1302pub struct CilExtProfiler {
1303    pub(super) timings: Vec<CilExtPassTiming>,
1304}
1305impl CilExtProfiler {
1306    pub fn new() -> Self {
1307        CilExtProfiler::default()
1308    }
1309    pub fn record(&mut self, t: CilExtPassTiming) {
1310        self.timings.push(t);
1311    }
1312    pub fn total_elapsed_us(&self) -> u64 {
1313        self.timings.iter().map(|t| t.elapsed_us).sum()
1314    }
1315    pub fn slowest_pass(&self) -> Option<&CilExtPassTiming> {
1316        self.timings.iter().max_by_key(|t| t.elapsed_us)
1317    }
1318    pub fn num_passes(&self) -> usize {
1319        self.timings.len()
1320    }
1321    pub fn profitable_passes(&self) -> Vec<&CilExtPassTiming> {
1322        self.timings.iter().filter(|t| t.is_profitable()).collect()
1323    }
1324}
1325/// A CIL method definition.
1326#[derive(Debug, Clone)]
1327pub struct CilMethod {
1328    pub name: std::string::String,
1329    pub params: Vec<(std::string::String, CilType)>,
1330    pub return_type: CilType,
1331    pub locals: Vec<CilLocal>,
1332    pub instructions: Vec<CilInstr>,
1333    pub is_static: bool,
1334    pub is_virtual: bool,
1335    pub is_abstract: bool,
1336    pub visibility: CilVisibility,
1337    pub max_stack: u32,
1338    pub custom_attrs: Vec<std::string::String>,
1339}
1340impl CilMethod {
1341    /// Create a new static method.
1342    pub fn new_static(name: impl Into<std::string::String>, return_type: CilType) -> Self {
1343        CilMethod {
1344            name: name.into(),
1345            params: Vec::new(),
1346            return_type,
1347            locals: Vec::new(),
1348            instructions: Vec::new(),
1349            is_static: true,
1350            is_virtual: false,
1351            is_abstract: false,
1352            visibility: CilVisibility::Public,
1353            max_stack: 8,
1354            custom_attrs: Vec::new(),
1355        }
1356    }
1357    /// Create a new instance method.
1358    pub fn new_instance(name: impl Into<std::string::String>, return_type: CilType) -> Self {
1359        CilMethod {
1360            name: name.into(),
1361            params: Vec::new(),
1362            return_type,
1363            locals: Vec::new(),
1364            instructions: Vec::new(),
1365            is_static: false,
1366            is_virtual: false,
1367            is_abstract: false,
1368            visibility: CilVisibility::Public,
1369            max_stack: 8,
1370            custom_attrs: Vec::new(),
1371        }
1372    }
1373    /// Add a parameter and return its index.
1374    pub fn add_param(&mut self, name: impl Into<std::string::String>, ty: CilType) -> u16 {
1375        let idx = self.params.len() as u16;
1376        self.params.push((name.into(), ty));
1377        idx
1378    }
1379    /// Add a local variable and return its index.
1380    pub fn add_local(&mut self, ty: CilType, name: Option<std::string::String>) -> u16 {
1381        let idx = self.locals.len() as u16;
1382        self.locals.push(CilLocal {
1383            index: idx,
1384            ty,
1385            name,
1386        });
1387        idx
1388    }
1389    /// Emit an instruction.
1390    pub fn emit(&mut self, instr: CilInstr) {
1391        self.instructions.push(instr);
1392    }
1393    /// Emit a label.
1394    pub fn emit_label(&mut self, label: impl Into<std::string::String>) {
1395        self.instructions.push(CilInstr::Label(label.into()));
1396    }
1397}
1398/// Visibility of a CIL method or field.
1399#[derive(Debug, Clone, PartialEq, Eq)]
1400pub enum CilVisibility {
1401    Private,
1402    Assembly,
1403    Family,
1404    Public,
1405}
1406/// Heuristic freshness key for CilExt incremental compilation.
1407#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1408pub struct CilExtIncrKey {
1409    pub content_hash: u64,
1410    pub config_hash: u64,
1411}
1412impl CilExtIncrKey {
1413    pub fn new(content: u64, config: u64) -> Self {
1414        CilExtIncrKey {
1415            content_hash: content,
1416            config_hash: config,
1417        }
1418    }
1419    pub fn combined_hash(&self) -> u64 {
1420        self.content_hash.wrapping_mul(0x9e3779b97f4a7c15) ^ self.config_hash
1421    }
1422    pub fn matches(&self, other: &CilExtIncrKey) -> bool {
1423        self.content_hash == other.content_hash && self.config_hash == other.config_hash
1424    }
1425}
1426/// Severity of a CilExt diagnostic.
1427#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1428pub enum CilExtDiagSeverity {
1429    Note,
1430    Warning,
1431    Error,
1432}
1433/// A version tag for CilExt output artifacts.
1434#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1435pub struct CilExtVersion {
1436    pub major: u32,
1437    pub minor: u32,
1438    pub patch: u32,
1439    pub pre: Option<String>,
1440}
1441impl CilExtVersion {
1442    pub fn new(major: u32, minor: u32, patch: u32) -> Self {
1443        CilExtVersion {
1444            major,
1445            minor,
1446            patch,
1447            pre: None,
1448        }
1449    }
1450    pub fn with_pre(mut self, pre: impl Into<String>) -> Self {
1451        self.pre = Some(pre.into());
1452        self
1453    }
1454    pub fn is_stable(&self) -> bool {
1455        self.pre.is_none()
1456    }
1457    pub fn is_compatible_with(&self, other: &CilExtVersion) -> bool {
1458        self.major == other.major && self.minor >= other.minor
1459    }
1460}
1461/// A CIL class definition.
1462#[derive(Debug, Clone)]
1463pub struct CilClass {
1464    pub name: std::string::String,
1465    pub namespace: std::string::String,
1466    pub fields: Vec<CilField>,
1467    pub methods: Vec<CilMethod>,
1468    pub interfaces: Vec<CilType>,
1469    pub base_type: Option<CilType>,
1470    pub is_value_type: bool,
1471    pub is_abstract: bool,
1472    pub is_sealed: bool,
1473    pub is_interface: bool,
1474    pub visibility: CilVisibility,
1475    pub type_params: Vec<std::string::String>,
1476    pub custom_attrs: Vec<std::string::String>,
1477    pub nested: Vec<CilClass>,
1478}
1479impl CilClass {
1480    /// Create a new public class.
1481    pub fn new(
1482        namespace: impl Into<std::string::String>,
1483        name: impl Into<std::string::String>,
1484    ) -> Self {
1485        CilClass {
1486            name: name.into(),
1487            namespace: namespace.into(),
1488            fields: Vec::new(),
1489            methods: Vec::new(),
1490            interfaces: Vec::new(),
1491            base_type: None,
1492            is_value_type: false,
1493            is_abstract: false,
1494            is_sealed: false,
1495            is_interface: false,
1496            visibility: CilVisibility::Public,
1497            type_params: Vec::new(),
1498            custom_attrs: Vec::new(),
1499            nested: Vec::new(),
1500        }
1501    }
1502    /// Add a method.
1503    pub fn add_method(&mut self, method: CilMethod) {
1504        self.methods.push(method);
1505    }
1506    /// Add a field.
1507    pub fn add_field(&mut self, field: CilField) {
1508        self.fields.push(field);
1509    }
1510    /// Add a nested class.
1511    pub fn add_nested(&mut self, nested: CilClass) {
1512        self.nested.push(nested);
1513    }
1514    /// Get the fully qualified type name.
1515    pub fn full_name(&self) -> std::string::String {
1516        if self.namespace.is_empty() {
1517            self.name.clone()
1518        } else {
1519            format!("{}.{}", self.namespace, self.name)
1520        }
1521    }
1522    /// Find a method by name.
1523    pub fn find_method(&self, name: &str) -> Option<&CilMethod> {
1524        self.methods.iter().find(|m| m.name == name)
1525    }
1526}
1527/// A .NET assembly containing one or more classes.
1528#[derive(Debug, Clone)]
1529pub struct CilAssembly {
1530    pub name: std::string::String,
1531    pub version: (u16, u16, u16, u16),
1532    pub classes: Vec<CilClass>,
1533    pub entry_point: Option<(std::string::String, std::string::String)>,
1534    pub custom_attrs: Vec<std::string::String>,
1535    pub references: Vec<std::string::String>,
1536    pub target_runtime: std::string::String,
1537}
1538impl CilAssembly {
1539    /// Create a new assembly.
1540    pub fn new(name: impl Into<std::string::String>) -> Self {
1541        CilAssembly {
1542            name: name.into(),
1543            version: (1, 0, 0, 0),
1544            classes: Vec::new(),
1545            entry_point: None,
1546            custom_attrs: Vec::new(),
1547            references: vec!["mscorlib".to_string()],
1548            target_runtime: "v4.0.30319".to_string(),
1549        }
1550    }
1551    /// Add a class.
1552    pub fn add_class(&mut self, class: CilClass) {
1553        self.classes.push(class);
1554    }
1555    /// Find a class by full name.
1556    pub fn find_class(&self, full_name: &str) -> Option<&CilClass> {
1557        self.classes.iter().find(|c| c.full_name() == full_name)
1558    }
1559    /// Set the entry point.
1560    pub fn set_entry_point(
1561        &mut self,
1562        class_name: impl Into<std::string::String>,
1563        method_name: impl Into<std::string::String>,
1564    ) {
1565        self.entry_point = Some((class_name.into(), method_name.into()));
1566    }
1567}
1568/// A diagnostic message from a CilExt pass.
1569#[derive(Debug, Clone)]
1570pub struct CilExtDiagMsg {
1571    pub severity: CilExtDiagSeverity,
1572    pub pass: String,
1573    pub message: String,
1574}
1575impl CilExtDiagMsg {
1576    pub fn error(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1577        CilExtDiagMsg {
1578            severity: CilExtDiagSeverity::Error,
1579            pass: pass.into(),
1580            message: msg.into(),
1581        }
1582    }
1583    pub fn warning(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1584        CilExtDiagMsg {
1585            severity: CilExtDiagSeverity::Warning,
1586            pass: pass.into(),
1587            message: msg.into(),
1588        }
1589    }
1590    pub fn note(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1591        CilExtDiagMsg {
1592            severity: CilExtDiagSeverity::Note,
1593            pass: pass.into(),
1594            message: msg.into(),
1595        }
1596    }
1597}
1598/// A generic key-value configuration store for CilExt.
1599#[derive(Debug, Clone, Default)]
1600pub struct CilExtConfig {
1601    pub(super) entries: std::collections::HashMap<String, String>,
1602}
1603impl CilExtConfig {
1604    pub fn new() -> Self {
1605        CilExtConfig::default()
1606    }
1607    pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
1608        self.entries.insert(key.into(), value.into());
1609    }
1610    pub fn get(&self, key: &str) -> Option<&str> {
1611        self.entries.get(key).map(|s| s.as_str())
1612    }
1613    pub fn get_bool(&self, key: &str) -> bool {
1614        matches!(self.get(key), Some("true") | Some("1") | Some("yes"))
1615    }
1616    pub fn get_int(&self, key: &str) -> Option<i64> {
1617        self.get(key)?.parse().ok()
1618    }
1619    pub fn len(&self) -> usize {
1620        self.entries.len()
1621    }
1622    pub fn is_empty(&self) -> bool {
1623        self.entries.is_empty()
1624    }
1625}
1626/// Collects CilExt diagnostics.
1627#[derive(Debug, Default)]
1628pub struct CilExtDiagCollector {
1629    pub(super) msgs: Vec<CilExtDiagMsg>,
1630}
1631impl CilExtDiagCollector {
1632    pub fn new() -> Self {
1633        CilExtDiagCollector::default()
1634    }
1635    pub fn emit(&mut self, d: CilExtDiagMsg) {
1636        self.msgs.push(d);
1637    }
1638    pub fn has_errors(&self) -> bool {
1639        self.msgs
1640            .iter()
1641            .any(|d| d.severity == CilExtDiagSeverity::Error)
1642    }
1643    pub fn errors(&self) -> Vec<&CilExtDiagMsg> {
1644        self.msgs
1645            .iter()
1646            .filter(|d| d.severity == CilExtDiagSeverity::Error)
1647            .collect()
1648    }
1649    pub fn warnings(&self) -> Vec<&CilExtDiagMsg> {
1650        self.msgs
1651            .iter()
1652            .filter(|d| d.severity == CilExtDiagSeverity::Warning)
1653            .collect()
1654    }
1655    pub fn len(&self) -> usize {
1656        self.msgs.len()
1657    }
1658    pub fn is_empty(&self) -> bool {
1659        self.msgs.is_empty()
1660    }
1661    pub fn clear(&mut self) {
1662        self.msgs.clear();
1663    }
1664}
1665#[allow(dead_code)]
1666#[derive(Debug, Clone)]
1667pub struct CILWorklist {
1668    pub(super) items: std::collections::VecDeque<u32>,
1669    pub(super) in_worklist: std::collections::HashSet<u32>,
1670}
1671impl CILWorklist {
1672    #[allow(dead_code)]
1673    pub fn new() -> Self {
1674        CILWorklist {
1675            items: std::collections::VecDeque::new(),
1676            in_worklist: std::collections::HashSet::new(),
1677        }
1678    }
1679    #[allow(dead_code)]
1680    pub fn push(&mut self, item: u32) -> bool {
1681        if self.in_worklist.insert(item) {
1682            self.items.push_back(item);
1683            true
1684        } else {
1685            false
1686        }
1687    }
1688    #[allow(dead_code)]
1689    pub fn pop(&mut self) -> Option<u32> {
1690        let item = self.items.pop_front()?;
1691        self.in_worklist.remove(&item);
1692        Some(item)
1693    }
1694    #[allow(dead_code)]
1695    pub fn is_empty(&self) -> bool {
1696        self.items.is_empty()
1697    }
1698    #[allow(dead_code)]
1699    pub fn len(&self) -> usize {
1700        self.items.len()
1701    }
1702    #[allow(dead_code)]
1703    pub fn contains(&self, item: u32) -> bool {
1704        self.in_worklist.contains(&item)
1705    }
1706}
1707/// A reference to a .NET method for use in call instructions.
1708#[derive(Debug, Clone)]
1709pub struct CilMethodRef {
1710    pub call_conv: CilCallConv,
1711    pub return_type: CilType,
1712    pub declaring_type: CilType,
1713    pub name: std::string::String,
1714    pub param_types: Vec<CilType>,
1715}
1716/// An indirect call signature.
1717#[derive(Debug, Clone)]
1718pub struct CilCallSig {
1719    pub call_conv: CilCallConv,
1720    pub return_type: CilType,
1721    pub param_types: Vec<CilType>,
1722}
1723/// Emission statistics for CilExt.
1724#[derive(Debug, Clone, Default)]
1725pub struct CilExtEmitStats {
1726    pub bytes_emitted: usize,
1727    pub items_emitted: usize,
1728    pub errors: usize,
1729    pub warnings: usize,
1730    pub elapsed_ms: u64,
1731}
1732impl CilExtEmitStats {
1733    pub fn new() -> Self {
1734        CilExtEmitStats::default()
1735    }
1736    pub fn throughput_bps(&self) -> f64 {
1737        if self.elapsed_ms == 0 {
1738            0.0
1739        } else {
1740            self.bytes_emitted as f64 / (self.elapsed_ms as f64 / 1000.0)
1741        }
1742    }
1743    pub fn is_clean(&self) -> bool {
1744        self.errors == 0
1745    }
1746}
1747#[allow(dead_code)]
1748#[derive(Debug, Clone)]
1749pub struct CILDominatorTree {
1750    pub idom: Vec<Option<u32>>,
1751    pub dom_children: Vec<Vec<u32>>,
1752    pub dom_depth: Vec<u32>,
1753}
1754impl CILDominatorTree {
1755    #[allow(dead_code)]
1756    pub fn new(size: usize) -> Self {
1757        CILDominatorTree {
1758            idom: vec![None; size],
1759            dom_children: vec![Vec::new(); size],
1760            dom_depth: vec![0; size],
1761        }
1762    }
1763    #[allow(dead_code)]
1764    pub fn set_idom(&mut self, node: usize, idom: u32) {
1765        self.idom[node] = Some(idom);
1766    }
1767    #[allow(dead_code)]
1768    pub fn dominates(&self, a: usize, b: usize) -> bool {
1769        if a == b {
1770            return true;
1771        }
1772        let mut cur = b;
1773        loop {
1774            match self.idom[cur] {
1775                Some(parent) if parent as usize == a => return true,
1776                Some(parent) if parent as usize == cur => return false,
1777                Some(parent) => cur = parent as usize,
1778                None => return false,
1779            }
1780        }
1781    }
1782    #[allow(dead_code)]
1783    pub fn depth(&self, node: usize) -> u32 {
1784        self.dom_depth.get(node).copied().unwrap_or(0)
1785    }
1786}
1787/// A field declaration in a CIL class.
1788#[derive(Debug, Clone)]
1789pub struct CilField {
1790    pub name: std::string::String,
1791    pub ty: CilType,
1792    pub is_static: bool,
1793    pub visibility: CilVisibility,
1794    pub init_value: Option<CilLiteral>,
1795}
1796/// Calling convention for CIL method references.
1797#[derive(Debug, Clone, PartialEq, Eq)]
1798pub enum CilCallConv {
1799    /// Static method
1800    Default,
1801    /// Instance method
1802    Instance,
1803    /// Generic instance method
1804    Generic(u32),
1805}
1806/// Pass-timing record for CilExt profiler.
1807#[derive(Debug, Clone)]
1808pub struct CilExtPassTiming {
1809    pub pass_name: String,
1810    pub elapsed_us: u64,
1811    pub items_processed: usize,
1812    pub bytes_before: usize,
1813    pub bytes_after: usize,
1814}
1815impl CilExtPassTiming {
1816    pub fn new(
1817        pass_name: impl Into<String>,
1818        elapsed_us: u64,
1819        items: usize,
1820        before: usize,
1821        after: usize,
1822    ) -> Self {
1823        CilExtPassTiming {
1824            pass_name: pass_name.into(),
1825            elapsed_us,
1826            items_processed: items,
1827            bytes_before: before,
1828            bytes_after: after,
1829        }
1830    }
1831    pub fn throughput_mps(&self) -> f64 {
1832        if self.elapsed_us == 0 {
1833            0.0
1834        } else {
1835            self.items_processed as f64 / (self.elapsed_us as f64 / 1_000_000.0)
1836        }
1837    }
1838    pub fn size_ratio(&self) -> f64 {
1839        if self.bytes_before == 0 {
1840            1.0
1841        } else {
1842            self.bytes_after as f64 / self.bytes_before as f64
1843        }
1844    }
1845    pub fn is_profitable(&self) -> bool {
1846        self.size_ratio() <= 1.05
1847    }
1848}
1849#[allow(dead_code)]
1850#[derive(Debug, Clone, Default)]
1851pub struct CILPassStats {
1852    pub total_runs: u32,
1853    pub successful_runs: u32,
1854    pub total_changes: u64,
1855    pub time_ms: u64,
1856    pub iterations_used: u32,
1857}
1858impl CILPassStats {
1859    #[allow(dead_code)]
1860    pub fn new() -> Self {
1861        Self::default()
1862    }
1863    #[allow(dead_code)]
1864    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1865        self.total_runs += 1;
1866        self.successful_runs += 1;
1867        self.total_changes += changes;
1868        self.time_ms += time_ms;
1869        self.iterations_used = iterations;
1870    }
1871    #[allow(dead_code)]
1872    pub fn average_changes_per_run(&self) -> f64 {
1873        if self.total_runs == 0 {
1874            return 0.0;
1875        }
1876        self.total_changes as f64 / self.total_runs as f64
1877    }
1878    #[allow(dead_code)]
1879    pub fn success_rate(&self) -> f64 {
1880        if self.total_runs == 0 {
1881            return 0.0;
1882        }
1883        self.successful_runs as f64 / self.total_runs as f64
1884    }
1885    #[allow(dead_code)]
1886    pub fn format_summary(&self) -> String {
1887        format!(
1888            "Runs: {}/{}, Changes: {}, Time: {}ms",
1889            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1890        )
1891    }
1892}