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::Int => CilType::Int64,
483            LcnfType::LcnfString => CilType::String,
484            LcnfType::Unit => CilType::Void,
485            LcnfType::Var(name) => match name.as_str() {
486                "Int32" | "int32" => CilType::Int32,
487                "Int64" | "int64" | "Int" => CilType::Int64,
488                "UInt32" | "uint32" => CilType::UInt32,
489                "Float" | "Float32" | "float32" => CilType::Float32,
490                "Float64" | "float64" | "Double" => CilType::Float64,
491                "Bool" | "bool" => CilType::Bool,
492                "String" | "string" => CilType::String,
493                "Unit" | "void" => CilType::Void,
494                "Char" | "char" => CilType::Char,
495                _ => CilType::class_in(&self.default_namespace, name.clone()),
496            },
497            LcnfType::Fun(params, ret) => {
498                if params.len() == 1 {
499                    CilType::func_of(
500                        self.lcnf_to_cil_type(&params[0]),
501                        self.lcnf_to_cil_type(ret),
502                    )
503                } else {
504                    CilType::class_in("System", "Delegate")
505                }
506            }
507            LcnfType::Ctor(name, _) => CilType::class_in(&self.default_namespace, name.clone()),
508        }
509    }
510    /// Emit CIL instructions for an LCNF literal.
511    pub fn emit_literal(&self, method: &mut CilMethod, lit: &LcnfLit) {
512        match lit {
513            LcnfLit::Nat(n) => method.emit(CilInstr::LdcI8(*n as i64)),
514            LcnfLit::Int(i) => method.emit(CilInstr::LdcI8(*i)),
515            LcnfLit::Str(s) => method.emit(CilInstr::Ldstr(s.clone())),
516        }
517    }
518    /// Emit CIL instructions for an LCNF argument.
519    pub fn emit_arg(&mut self, method: &mut CilMethod, arg: &LcnfArg) {
520        match arg {
521            LcnfArg::Var(id) => {
522                if let Some(&local_idx) = self.var_locals.get(&id.0) {
523                    method.emit(CilInstr::LdlocS(local_idx));
524                } else {
525                    method.emit(CilInstr::LdargS(0));
526                }
527            }
528            LcnfArg::Lit(lit) => self.emit_literal(method, lit),
529            LcnfArg::Erased | LcnfArg::Type(_) => method.emit(CilInstr::Ldnull),
530        }
531    }
532    /// Emit CIL instructions for an LCNF let-value.
533    pub fn emit_let_value(&mut self, method: &mut CilMethod, val: &LcnfLetValue) {
534        match val {
535            LcnfLetValue::App(func, args) => {
536                for arg in args.iter() {
537                    self.emit_arg(method, arg);
538                }
539                self.emit_arg(method, func);
540                let invoke_ref = CilMethodRef {
541                    call_conv: CilCallConv::Instance,
542                    return_type: CilType::Object,
543                    declaring_type: CilType::class_in("System", "Delegate"),
544                    name: "DynamicInvoke".to_string(),
545                    param_types: vec![CilType::Array(Box::new(CilType::Object))],
546                };
547                method.emit(CilInstr::Callvirt(invoke_ref));
548            }
549            LcnfLetValue::Proj(_struct_name, idx, var_id) => {
550                if let Some(&local_idx) = self.var_locals.get(&var_id.0) {
551                    method.emit(CilInstr::LdlocS(local_idx));
552                } else {
553                    method.emit(CilInstr::LdargS(0));
554                }
555                let field_ref = CilFieldRef {
556                    field_type: CilType::Object,
557                    declaring_type: CilType::Object,
558                    name: format!("_field{}", idx),
559                };
560                method.emit(CilInstr::Ldfld(field_ref));
561            }
562            LcnfLetValue::Ctor(name, _tag, args) => {
563                let ctor_type = CilType::class_in(self.default_namespace.clone(), name.clone());
564                let param_types: Vec<CilType> = args.iter().map(|_| CilType::Object).collect();
565                for arg in args.iter() {
566                    self.emit_arg(method, arg);
567                }
568                method.emit(CilInstr::Newobj(CilMethodRef {
569                    call_conv: CilCallConv::Instance,
570                    return_type: CilType::Void,
571                    declaring_type: ctor_type,
572                    name: ".ctor".to_string(),
573                    param_types,
574                }));
575            }
576            LcnfLetValue::Lit(lit) => self.emit_literal(method, lit),
577            LcnfLetValue::Erased => method.emit(CilInstr::Ldnull),
578            LcnfLetValue::FVar(id) => {
579                if let Some(&local_idx) = self.var_locals.get(&id.0) {
580                    method.emit(CilInstr::LdlocS(local_idx));
581                } else {
582                    method.emit(CilInstr::LdargS(0));
583                }
584            }
585            LcnfLetValue::Reset(var) => {
586                if let Some(&local_idx) = self.var_locals.get(&var.0) {
587                    method.emit(CilInstr::LdlocS(local_idx));
588                } else {
589                    method.emit(CilInstr::LdargS(0));
590                }
591                method.emit(CilInstr::Comment("reset (reuse optimization)".to_string()));
592            }
593            LcnfLetValue::Reuse(slot, name, _tag, args) => {
594                let ctor_type = CilType::class_in(self.default_namespace.clone(), name.clone());
595                let param_types: Vec<CilType> = args.iter().map(|_| CilType::Object).collect();
596                if let Some(&local_idx) = self.var_locals.get(&slot.0) {
597                    method.emit(CilInstr::LdlocS(local_idx));
598                } else {
599                    method.emit(CilInstr::LdargS(0));
600                }
601                for arg in args.iter() {
602                    self.emit_arg(method, arg);
603                }
604                method.emit(CilInstr::Comment(format!("reuse -> {}", name)));
605                method.emit(CilInstr::Newobj(CilMethodRef {
606                    call_conv: CilCallConv::Instance,
607                    return_type: CilType::Void,
608                    declaring_type: ctor_type,
609                    name: ".ctor".to_string(),
610                    param_types,
611                }));
612            }
613        }
614    }
615    /// Emit CIL instructions for an LCNF expression.
616    #[allow(clippy::too_many_arguments)]
617    pub fn emit_expr(&mut self, method: &mut CilMethod, expr: &LcnfExpr) {
618        match expr {
619            LcnfExpr::Let {
620                id,
621                ty,
622                value,
623                body,
624                ..
625            } => {
626                self.emit_let_value(method, value);
627                let cil_ty = self.lcnf_to_cil_type(ty);
628                let local_idx = method.add_local(cil_ty, None);
629                self.var_locals.insert(id.0, local_idx);
630                method.emit(CilInstr::StlocS(local_idx));
631                self.emit_expr(method, body);
632            }
633            LcnfExpr::Case {
634                scrutinee,
635                alts,
636                default,
637                ..
638            } => {
639                let end_label = self.fresh_label();
640                for alt in alts.iter() {
641                    let next_label = self.fresh_label();
642                    if let Some(&local_idx) = self.var_locals.get(&scrutinee.0) {
643                        method.emit(CilInstr::LdlocS(local_idx));
644                    } else {
645                        method.emit(CilInstr::LdargS(0));
646                    }
647                    let ctor_type =
648                        CilType::class_in(self.default_namespace.clone(), alt.ctor_name.clone());
649                    method.emit(CilInstr::Isinst(ctor_type.clone()));
650                    method.emit(CilInstr::Dup);
651                    method.emit(CilInstr::Brfalse(next_label.clone()));
652                    for (i, param) in alt.params.iter().enumerate() {
653                        method.emit(CilInstr::Dup);
654                        let field_ref = CilFieldRef {
655                            field_type: CilType::Object,
656                            declaring_type: ctor_type.clone(),
657                            name: format!("_field{}", i),
658                        };
659                        method.emit(CilInstr::Ldfld(field_ref));
660                        let param_ty = self.lcnf_to_cil_type(&param.ty);
661                        let local_idx = method.add_local(param_ty, Some(param.name.clone()));
662                        self.var_locals.insert(param.id.0, local_idx);
663                        method.emit(CilInstr::StlocS(local_idx));
664                    }
665                    method.emit(CilInstr::Pop);
666                    let body = alt.body.clone();
667                    self.emit_expr(method, &body);
668                    method.emit(CilInstr::Br(end_label.clone()));
669                    method.emit_label(next_label);
670                    method.emit(CilInstr::Pop);
671                }
672                if let Some(def_body) = default {
673                    let def_body = def_body.clone();
674                    self.emit_expr(method, &def_body);
675                } else {
676                    method.emit(CilInstr::Ldstr("MatchFailure".to_string()));
677                    method.emit(CilInstr::Newobj(CilMethodRef {
678                        call_conv: CilCallConv::Instance,
679                        return_type: CilType::Void,
680                        declaring_type: CilType::class_in("System", "Exception"),
681                        name: ".ctor".to_string(),
682                        param_types: vec![CilType::String],
683                    }));
684                    method.emit(CilInstr::Throw);
685                }
686                method.emit_label(end_label);
687            }
688            LcnfExpr::Return(arg) => {
689                self.emit_arg(method, arg);
690            }
691            LcnfExpr::Unreachable => {
692                method.emit(CilInstr::Ldstr("unreachable".to_string()));
693                method.emit(CilInstr::Newobj(CilMethodRef {
694                    call_conv: CilCallConv::Instance,
695                    return_type: CilType::Void,
696                    declaring_type: CilType::class_in("System", "InvalidOperationException"),
697                    name: ".ctor".to_string(),
698                    param_types: vec![CilType::String],
699                }));
700                method.emit(CilInstr::Throw);
701            }
702            LcnfExpr::TailCall(func, args) => {
703                for arg in args.iter() {
704                    self.emit_arg(method, arg);
705                }
706                self.emit_arg(method, func);
707                let invoke_ref = CilMethodRef {
708                    call_conv: CilCallConv::Instance,
709                    return_type: CilType::Object,
710                    declaring_type: CilType::class_in("System", "Delegate"),
711                    name: "DynamicInvoke".to_string(),
712                    param_types: vec![CilType::Array(Box::new(CilType::Object))],
713                };
714                method.emit(CilInstr::TailCall(invoke_ref));
715            }
716        }
717    }
718    /// Emit a complete LCNF function declaration as a CIL method.
719    pub fn emit_fun_decl(&mut self, decl: &LcnfFunDecl) -> CilMethod {
720        let ret_ty = self.lcnf_to_cil_type(&decl.ret_type);
721        let mut method = CilMethod::new_static(&decl.name, ret_ty);
722        for param in &decl.params {
723            let cil_ty = self.lcnf_to_cil_type(&param.ty);
724            let idx = method.add_param(param.name.clone(), cil_ty);
725            self.var_locals.insert(param.id.0, idx);
726        }
727        let body = decl.body.clone();
728        self.emit_expr(&mut method, &body);
729        method.emit(CilInstr::Ret);
730        method
731    }
732    /// Emit the assembly as IL assembly source text (for `ilasm`).
733    pub fn emit_ilasm(&self) -> std::string::String {
734        let mut out = std::string::String::new();
735        out.push_str(".assembly extern mscorlib {}\n");
736        out.push_str(&format!(".assembly '{}'\n{{\n", self.assembly.name));
737        let (maj, min, bld, rev) = self.assembly.version;
738        out.push_str(&format!("  .ver {}:{}:{}:{}\n", maj, min, bld, rev));
739        out.push_str("}\n\n");
740        out.push_str(&format!(".module '{}.exe'\n\n", self.assembly.name));
741        for class in &self.assembly.classes {
742            out.push_str(&self.emit_class_ilasm(class));
743            out.push('\n');
744        }
745        out
746    }
747    /// Emit a single class in IL assembly syntax.
748    pub(super) fn emit_class_ilasm(&self, class: &CilClass) -> std::string::String {
749        let mut out = std::string::String::new();
750        let vis = class.visibility.to_string();
751        let kind = if class.is_interface {
752            "interface"
753        } else if class.is_value_type {
754            "value class"
755        } else {
756            "class"
757        };
758        let sealed = if class.is_sealed { " sealed" } else { "" };
759        let abst = if class.is_abstract { " abstract" } else { "" };
760        out.push_str(&format!(
761            ".class {} {}{}{} {}\n{{\n",
762            vis,
763            kind,
764            sealed,
765            abst,
766            class.full_name()
767        ));
768        if let Some(base) = &class.base_type {
769            out.push_str(&format!("  extends {}\n", base));
770        }
771        for field in &class.fields {
772            let static_kw = if field.is_static { "static " } else { "" };
773            out.push_str(&format!(
774                "  .field {} {}{} '{}'\n",
775                field.visibility, static_kw, field.ty, field.name
776            ));
777        }
778        for method in &class.methods {
779            out.push_str(&self.emit_method_ilasm(method));
780        }
781        out.push_str("} // end of class\n");
782        out
783    }
784    /// Emit a single method in IL assembly syntax.
785    pub(super) fn emit_method_ilasm(&self, method: &CilMethod) -> std::string::String {
786        let mut out = std::string::String::new();
787        let static_kw = if method.is_static {
788            "static "
789        } else {
790            "instance "
791        };
792        let virtual_kw = if method.is_virtual { "virtual " } else { "" };
793        let vis = method.visibility.to_string();
794        let params_str = method
795            .params
796            .iter()
797            .map(|(name, ty)| format!("{} '{}'", ty, name))
798            .collect::<Vec<_>>()
799            .join(", ");
800        out.push_str(&format!(
801            "  .method {} {}{}{} '{}'({}) cil managed\n  {{\n",
802            vis, static_kw, virtual_kw, method.return_type, method.name, params_str
803        ));
804        if let Some((_, ref ep_method)) = self.assembly.entry_point {
805            if ep_method == &method.name {
806                out.push_str("    .entrypoint\n");
807            }
808        }
809        out.push_str(&format!("    .maxstack {}\n", method.max_stack));
810        if !method.locals.is_empty() {
811            out.push_str("    .locals init (");
812            for (i, local) in method.locals.iter().enumerate() {
813                if i > 0 {
814                    out.push_str(", ");
815                }
816                let name_str = local
817                    .name
818                    .as_deref()
819                    .map(|n| format!(" '{}'", n))
820                    .unwrap_or_default();
821                out.push_str(&format!("[{}] {}{}", i, local.ty, name_str));
822            }
823            out.push_str(")\n");
824        }
825        for instr in &method.instructions {
826            match instr {
827                CilInstr::Label(lbl) => out.push_str(&format!("  {}:\n", lbl)),
828                CilInstr::Comment(s) => out.push_str(&format!("    // {}\n", s)),
829                _ => out.push_str(&format!("    {}\n", emit_cil_instr(instr))),
830            }
831        }
832        out.push_str("  } // end of method\n");
833        out
834    }
835}
836#[allow(dead_code)]
837pub struct CILConstantFoldingHelper;
838impl CILConstantFoldingHelper {
839    #[allow(dead_code)]
840    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
841        a.checked_add(b)
842    }
843    #[allow(dead_code)]
844    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
845        a.checked_sub(b)
846    }
847    #[allow(dead_code)]
848    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
849        a.checked_mul(b)
850    }
851    #[allow(dead_code)]
852    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
853        if b == 0 {
854            None
855        } else {
856            a.checked_div(b)
857        }
858    }
859    #[allow(dead_code)]
860    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
861        a + b
862    }
863    #[allow(dead_code)]
864    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
865        a * b
866    }
867    #[allow(dead_code)]
868    pub fn fold_neg_i64(a: i64) -> Option<i64> {
869        a.checked_neg()
870    }
871    #[allow(dead_code)]
872    pub fn fold_not_bool(a: bool) -> bool {
873        !a
874    }
875    #[allow(dead_code)]
876    pub fn fold_and_bool(a: bool, b: bool) -> bool {
877        a && b
878    }
879    #[allow(dead_code)]
880    pub fn fold_or_bool(a: bool, b: bool) -> bool {
881        a || b
882    }
883    #[allow(dead_code)]
884    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
885        a.checked_shl(b)
886    }
887    #[allow(dead_code)]
888    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
889        a.checked_shr(b)
890    }
891    #[allow(dead_code)]
892    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
893        if b == 0 {
894            None
895        } else {
896            Some(a % b)
897        }
898    }
899    #[allow(dead_code)]
900    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
901        a & b
902    }
903    #[allow(dead_code)]
904    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
905        a | b
906    }
907    #[allow(dead_code)]
908    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
909        a ^ b
910    }
911    #[allow(dead_code)]
912    pub fn fold_bitnot_i64(a: i64) -> i64 {
913        !a
914    }
915}
916#[allow(dead_code)]
917#[derive(Debug, Clone)]
918pub struct CILCacheEntry {
919    pub key: String,
920    pub data: Vec<u8>,
921    pub timestamp: u64,
922    pub valid: bool,
923}
924#[allow(dead_code)]
925#[derive(Debug, Clone)]
926pub struct CILLivenessInfo {
927    pub live_in: Vec<std::collections::HashSet<u32>>,
928    pub live_out: Vec<std::collections::HashSet<u32>>,
929    pub defs: Vec<std::collections::HashSet<u32>>,
930    pub uses: Vec<std::collections::HashSet<u32>>,
931}
932impl CILLivenessInfo {
933    #[allow(dead_code)]
934    pub fn new(block_count: usize) -> Self {
935        CILLivenessInfo {
936            live_in: vec![std::collections::HashSet::new(); block_count],
937            live_out: vec![std::collections::HashSet::new(); block_count],
938            defs: vec![std::collections::HashSet::new(); block_count],
939            uses: vec![std::collections::HashSet::new(); block_count],
940        }
941    }
942    #[allow(dead_code)]
943    pub fn add_def(&mut self, block: usize, var: u32) {
944        if block < self.defs.len() {
945            self.defs[block].insert(var);
946        }
947    }
948    #[allow(dead_code)]
949    pub fn add_use(&mut self, block: usize, var: u32) {
950        if block < self.uses.len() {
951            self.uses[block].insert(var);
952        }
953    }
954    #[allow(dead_code)]
955    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
956        self.live_in
957            .get(block)
958            .map(|s| s.contains(&var))
959            .unwrap_or(false)
960    }
961    #[allow(dead_code)]
962    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
963        self.live_out
964            .get(block)
965            .map(|s| s.contains(&var))
966            .unwrap_or(false)
967    }
968}
969/// A feature flag set for CilExt capabilities.
970#[derive(Debug, Clone, Default)]
971pub struct CilExtFeatures {
972    pub(super) flags: std::collections::HashSet<String>,
973}
974impl CilExtFeatures {
975    pub fn new() -> Self {
976        CilExtFeatures::default()
977    }
978    pub fn enable(&mut self, flag: impl Into<String>) {
979        self.flags.insert(flag.into());
980    }
981    pub fn disable(&mut self, flag: &str) {
982        self.flags.remove(flag);
983    }
984    pub fn is_enabled(&self, flag: &str) -> bool {
985        self.flags.contains(flag)
986    }
987    pub fn len(&self) -> usize {
988        self.flags.len()
989    }
990    pub fn is_empty(&self) -> bool {
991        self.flags.is_empty()
992    }
993    pub fn union(&self, other: &CilExtFeatures) -> CilExtFeatures {
994        CilExtFeatures {
995            flags: self.flags.union(&other.flags).cloned().collect(),
996        }
997    }
998    pub fn intersection(&self, other: &CilExtFeatures) -> CilExtFeatures {
999        CilExtFeatures {
1000            flags: self.flags.intersection(&other.flags).cloned().collect(),
1001        }
1002    }
1003}
1004/// A local variable declaration within a CIL method.
1005#[derive(Debug, Clone)]
1006pub struct CilLocal {
1007    pub index: u16,
1008    pub ty: CilType,
1009    pub name: Option<std::string::String>,
1010}
1011#[allow(dead_code)]
1012#[derive(Debug, Clone)]
1013pub struct CILPassConfig {
1014    pub phase: CILPassPhase,
1015    pub enabled: bool,
1016    pub max_iterations: u32,
1017    pub debug_output: bool,
1018    pub pass_name: String,
1019}
1020impl CILPassConfig {
1021    #[allow(dead_code)]
1022    pub fn new(name: impl Into<String>, phase: CILPassPhase) -> Self {
1023        CILPassConfig {
1024            phase,
1025            enabled: true,
1026            max_iterations: 10,
1027            debug_output: false,
1028            pass_name: name.into(),
1029        }
1030    }
1031    #[allow(dead_code)]
1032    pub fn disabled(mut self) -> Self {
1033        self.enabled = false;
1034        self
1035    }
1036    #[allow(dead_code)]
1037    pub fn with_debug(mut self) -> Self {
1038        self.debug_output = true;
1039        self
1040    }
1041    #[allow(dead_code)]
1042    pub fn max_iter(mut self, n: u32) -> Self {
1043        self.max_iterations = n;
1044        self
1045    }
1046}
1047#[allow(dead_code)]
1048#[derive(Debug, Clone)]
1049pub struct CILAnalysisCache {
1050    pub(super) entries: std::collections::HashMap<String, CILCacheEntry>,
1051    pub(super) max_size: usize,
1052    pub(super) hits: u64,
1053    pub(super) misses: u64,
1054}
1055impl CILAnalysisCache {
1056    #[allow(dead_code)]
1057    pub fn new(max_size: usize) -> Self {
1058        CILAnalysisCache {
1059            entries: std::collections::HashMap::new(),
1060            max_size,
1061            hits: 0,
1062            misses: 0,
1063        }
1064    }
1065    #[allow(dead_code)]
1066    pub fn get(&mut self, key: &str) -> Option<&CILCacheEntry> {
1067        if self.entries.contains_key(key) {
1068            self.hits += 1;
1069            self.entries.get(key)
1070        } else {
1071            self.misses += 1;
1072            None
1073        }
1074    }
1075    #[allow(dead_code)]
1076    pub fn insert(&mut self, key: String, data: Vec<u8>) {
1077        if self.entries.len() >= self.max_size {
1078            if let Some(oldest) = self.entries.keys().next().cloned() {
1079                self.entries.remove(&oldest);
1080            }
1081        }
1082        self.entries.insert(
1083            key.clone(),
1084            CILCacheEntry {
1085                key,
1086                data,
1087                timestamp: 0,
1088                valid: true,
1089            },
1090        );
1091    }
1092    #[allow(dead_code)]
1093    pub fn invalidate(&mut self, key: &str) {
1094        if let Some(entry) = self.entries.get_mut(key) {
1095            entry.valid = false;
1096        }
1097    }
1098    #[allow(dead_code)]
1099    pub fn clear(&mut self) {
1100        self.entries.clear();
1101    }
1102    #[allow(dead_code)]
1103    pub fn hit_rate(&self) -> f64 {
1104        let total = self.hits + self.misses;
1105        if total == 0 {
1106            return 0.0;
1107        }
1108        self.hits as f64 / total as f64
1109    }
1110    #[allow(dead_code)]
1111    pub fn size(&self) -> usize {
1112        self.entries.len()
1113    }
1114}
1115/// CIL type representation.
1116///
1117/// Covers all types available in the .NET Common Type System (CTS).
1118#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1119pub enum CilType {
1120    /// `void` — no return value
1121    Void,
1122    /// `bool` — System.Boolean (1 byte)
1123    Bool,
1124    /// `int8` — System.SByte
1125    Int8,
1126    /// `int16` — System.Int16
1127    Int16,
1128    /// `int32` — System.Int32
1129    Int32,
1130    /// `int64` — System.Int64
1131    Int64,
1132    /// `uint8` — System.Byte
1133    UInt8,
1134    /// `uint16` — System.UInt16
1135    UInt16,
1136    /// `uint32` — System.UInt32
1137    UInt32,
1138    /// `uint64` — System.UInt64
1139    UInt64,
1140    /// `float32` — System.Single
1141    Float32,
1142    /// `float64` — System.Double
1143    Float64,
1144    /// `char` — System.Char (UTF-16 code unit)
1145    Char,
1146    /// `string` — System.String (immutable UTF-16 string)
1147    String,
1148    /// `object` — System.Object (root of the type hierarchy)
1149    Object,
1150    /// `class [Assembly]Namespace.TypeName`
1151    Class {
1152        assembly: Option<std::string::String>,
1153        namespace: std::string::String,
1154        name: std::string::String,
1155    },
1156    /// `valuetype [Assembly]Namespace.TypeName`
1157    ValueType {
1158        assembly: Option<std::string::String>,
1159        namespace: std::string::String,
1160        name: std::string::String,
1161    },
1162    /// `T[]` — single-dimensional array (szarray)
1163    Array(Box<CilType>),
1164    /// `T[,]` — multi-dimensional array
1165    MdArray(Box<CilType>, u32),
1166    /// `T*` — unmanaged pointer
1167    Ptr(Box<CilType>),
1168    /// `T&` — managed reference (byref)
1169    ByRef(Box<CilType>),
1170    /// Generic instance: `class MyType<T1, T2>`
1171    Generic(Box<CilType>, Vec<CilType>),
1172    /// Generic parameter `!T` (class-level)
1173    GenericParam(u32),
1174    /// Generic method parameter `!!T`
1175    GenericMethodParam(u32),
1176    /// `native int` — platform-native integer
1177    NativeInt,
1178    /// `native uint`
1179    NativeUInt,
1180}
1181impl CilType {
1182    /// Returns `true` if this is a value type (not a reference type).
1183    pub fn is_value_type(&self) -> bool {
1184        matches!(
1185            self,
1186            CilType::Bool
1187                | CilType::Int8
1188                | CilType::Int16
1189                | CilType::Int32
1190                | CilType::Int64
1191                | CilType::UInt8
1192                | CilType::UInt16
1193                | CilType::UInt32
1194                | CilType::UInt64
1195                | CilType::Float32
1196                | CilType::Float64
1197                | CilType::Char
1198                | CilType::NativeInt
1199                | CilType::NativeUInt
1200                | CilType::ValueType { .. }
1201        )
1202    }
1203    /// Returns `true` if this is a reference type.
1204    pub fn is_reference_type(&self) -> bool {
1205        !self.is_value_type()
1206    }
1207    /// Return the fully-qualified boxed name for use in generics.
1208    pub fn boxed_name(&self) -> &'static str {
1209        match self {
1210            CilType::Bool => "System.Boolean",
1211            CilType::Int8 => "System.SByte",
1212            CilType::Int16 => "System.Int16",
1213            CilType::Int32 => "System.Int32",
1214            CilType::Int64 => "System.Int64",
1215            CilType::Float32 => "System.Single",
1216            CilType::Float64 => "System.Double",
1217            CilType::Char => "System.Char",
1218            _ => "System.Object",
1219        }
1220    }
1221    /// Helper: create a `class` type in the given namespace.
1222    pub fn class_in(
1223        namespace: impl Into<std::string::String>,
1224        name: impl Into<std::string::String>,
1225    ) -> Self {
1226        CilType::Class {
1227            assembly: None,
1228            namespace: namespace.into(),
1229            name: name.into(),
1230        }
1231    }
1232    /// Helper: `System.Collections.Generic.List<T>`.
1233    pub fn list_of(elem: CilType) -> Self {
1234        CilType::Generic(
1235            Box::new(CilType::class_in("System.Collections.Generic", "List`1")),
1236            vec![elem],
1237        )
1238    }
1239    /// Helper: `System.Func<TArg, TResult>`.
1240    pub fn func_of(arg: CilType, result: CilType) -> Self {
1241        CilType::Generic(
1242            Box::new(CilType::class_in("System", "Func`2")),
1243            vec![arg, result],
1244        )
1245    }
1246}
1247/// A text buffer for building CilExt output source code.
1248#[derive(Debug, Default)]
1249pub struct CilExtSourceBuffer {
1250    pub(super) buf: String,
1251    pub(super) indent_level: usize,
1252    pub(super) indent_str: String,
1253}
1254impl CilExtSourceBuffer {
1255    pub fn new() -> Self {
1256        CilExtSourceBuffer {
1257            buf: String::new(),
1258            indent_level: 0,
1259            indent_str: "    ".to_string(),
1260        }
1261    }
1262    pub fn with_indent(mut self, indent: impl Into<String>) -> Self {
1263        self.indent_str = indent.into();
1264        self
1265    }
1266    pub fn push_line(&mut self, line: &str) {
1267        for _ in 0..self.indent_level {
1268            self.buf.push_str(&self.indent_str);
1269        }
1270        self.buf.push_str(line);
1271        self.buf.push('\n');
1272    }
1273    pub fn push_raw(&mut self, s: &str) {
1274        self.buf.push_str(s);
1275    }
1276    pub fn indent(&mut self) {
1277        self.indent_level += 1;
1278    }
1279    pub fn dedent(&mut self) {
1280        self.indent_level = self.indent_level.saturating_sub(1);
1281    }
1282    pub fn as_str(&self) -> &str {
1283        &self.buf
1284    }
1285    pub fn len(&self) -> usize {
1286        self.buf.len()
1287    }
1288    pub fn is_empty(&self) -> bool {
1289        self.buf.is_empty()
1290    }
1291    pub fn line_count(&self) -> usize {
1292        self.buf.lines().count()
1293    }
1294    pub fn into_string(self) -> String {
1295        self.buf
1296    }
1297    pub fn reset(&mut self) {
1298        self.buf.clear();
1299        self.indent_level = 0;
1300    }
1301}
1302/// Pipeline profiler for CilExt.
1303#[derive(Debug, Default)]
1304pub struct CilExtProfiler {
1305    pub(super) timings: Vec<CilExtPassTiming>,
1306}
1307impl CilExtProfiler {
1308    pub fn new() -> Self {
1309        CilExtProfiler::default()
1310    }
1311    pub fn record(&mut self, t: CilExtPassTiming) {
1312        self.timings.push(t);
1313    }
1314    pub fn total_elapsed_us(&self) -> u64 {
1315        self.timings.iter().map(|t| t.elapsed_us).sum()
1316    }
1317    pub fn slowest_pass(&self) -> Option<&CilExtPassTiming> {
1318        self.timings.iter().max_by_key(|t| t.elapsed_us)
1319    }
1320    pub fn num_passes(&self) -> usize {
1321        self.timings.len()
1322    }
1323    pub fn profitable_passes(&self) -> Vec<&CilExtPassTiming> {
1324        self.timings.iter().filter(|t| t.is_profitable()).collect()
1325    }
1326}
1327/// A CIL method definition.
1328#[derive(Debug, Clone)]
1329pub struct CilMethod {
1330    pub name: std::string::String,
1331    pub params: Vec<(std::string::String, CilType)>,
1332    pub return_type: CilType,
1333    pub locals: Vec<CilLocal>,
1334    pub instructions: Vec<CilInstr>,
1335    pub is_static: bool,
1336    pub is_virtual: bool,
1337    pub is_abstract: bool,
1338    pub visibility: CilVisibility,
1339    pub max_stack: u32,
1340    pub custom_attrs: Vec<std::string::String>,
1341}
1342impl CilMethod {
1343    /// Create a new static method.
1344    pub fn new_static(name: impl Into<std::string::String>, return_type: CilType) -> Self {
1345        CilMethod {
1346            name: name.into(),
1347            params: Vec::new(),
1348            return_type,
1349            locals: Vec::new(),
1350            instructions: Vec::new(),
1351            is_static: true,
1352            is_virtual: false,
1353            is_abstract: false,
1354            visibility: CilVisibility::Public,
1355            max_stack: 8,
1356            custom_attrs: Vec::new(),
1357        }
1358    }
1359    /// Create a new instance method.
1360    pub fn new_instance(name: impl Into<std::string::String>, return_type: CilType) -> Self {
1361        CilMethod {
1362            name: name.into(),
1363            params: Vec::new(),
1364            return_type,
1365            locals: Vec::new(),
1366            instructions: Vec::new(),
1367            is_static: false,
1368            is_virtual: false,
1369            is_abstract: false,
1370            visibility: CilVisibility::Public,
1371            max_stack: 8,
1372            custom_attrs: Vec::new(),
1373        }
1374    }
1375    /// Add a parameter and return its index.
1376    pub fn add_param(&mut self, name: impl Into<std::string::String>, ty: CilType) -> u16 {
1377        let idx = self.params.len() as u16;
1378        self.params.push((name.into(), ty));
1379        idx
1380    }
1381    /// Add a local variable and return its index.
1382    pub fn add_local(&mut self, ty: CilType, name: Option<std::string::String>) -> u16 {
1383        let idx = self.locals.len() as u16;
1384        self.locals.push(CilLocal {
1385            index: idx,
1386            ty,
1387            name,
1388        });
1389        idx
1390    }
1391    /// Emit an instruction.
1392    pub fn emit(&mut self, instr: CilInstr) {
1393        self.instructions.push(instr);
1394    }
1395    /// Emit a label.
1396    pub fn emit_label(&mut self, label: impl Into<std::string::String>) {
1397        self.instructions.push(CilInstr::Label(label.into()));
1398    }
1399}
1400/// Visibility of a CIL method or field.
1401#[derive(Debug, Clone, PartialEq, Eq)]
1402pub enum CilVisibility {
1403    Private,
1404    Assembly,
1405    Family,
1406    Public,
1407}
1408/// Heuristic freshness key for CilExt incremental compilation.
1409#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1410pub struct CilExtIncrKey {
1411    pub content_hash: u64,
1412    pub config_hash: u64,
1413}
1414impl CilExtIncrKey {
1415    pub fn new(content: u64, config: u64) -> Self {
1416        CilExtIncrKey {
1417            content_hash: content,
1418            config_hash: config,
1419        }
1420    }
1421    pub fn combined_hash(&self) -> u64 {
1422        self.content_hash.wrapping_mul(0x9e3779b97f4a7c15) ^ self.config_hash
1423    }
1424    pub fn matches(&self, other: &CilExtIncrKey) -> bool {
1425        self.content_hash == other.content_hash && self.config_hash == other.config_hash
1426    }
1427}
1428/// Severity of a CilExt diagnostic.
1429#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1430pub enum CilExtDiagSeverity {
1431    Note,
1432    Warning,
1433    Error,
1434}
1435/// A version tag for CilExt output artifacts.
1436#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1437pub struct CilExtVersion {
1438    pub major: u32,
1439    pub minor: u32,
1440    pub patch: u32,
1441    pub pre: Option<String>,
1442}
1443impl CilExtVersion {
1444    pub fn new(major: u32, minor: u32, patch: u32) -> Self {
1445        CilExtVersion {
1446            major,
1447            minor,
1448            patch,
1449            pre: None,
1450        }
1451    }
1452    pub fn with_pre(mut self, pre: impl Into<String>) -> Self {
1453        self.pre = Some(pre.into());
1454        self
1455    }
1456    pub fn is_stable(&self) -> bool {
1457        self.pre.is_none()
1458    }
1459    pub fn is_compatible_with(&self, other: &CilExtVersion) -> bool {
1460        self.major == other.major && self.minor >= other.minor
1461    }
1462}
1463/// A CIL class definition.
1464#[derive(Debug, Clone)]
1465pub struct CilClass {
1466    pub name: std::string::String,
1467    pub namespace: std::string::String,
1468    pub fields: Vec<CilField>,
1469    pub methods: Vec<CilMethod>,
1470    pub interfaces: Vec<CilType>,
1471    pub base_type: Option<CilType>,
1472    pub is_value_type: bool,
1473    pub is_abstract: bool,
1474    pub is_sealed: bool,
1475    pub is_interface: bool,
1476    pub visibility: CilVisibility,
1477    pub type_params: Vec<std::string::String>,
1478    pub custom_attrs: Vec<std::string::String>,
1479    pub nested: Vec<CilClass>,
1480}
1481impl CilClass {
1482    /// Create a new public class.
1483    pub fn new(
1484        namespace: impl Into<std::string::String>,
1485        name: impl Into<std::string::String>,
1486    ) -> Self {
1487        CilClass {
1488            name: name.into(),
1489            namespace: namespace.into(),
1490            fields: Vec::new(),
1491            methods: Vec::new(),
1492            interfaces: Vec::new(),
1493            base_type: None,
1494            is_value_type: false,
1495            is_abstract: false,
1496            is_sealed: false,
1497            is_interface: false,
1498            visibility: CilVisibility::Public,
1499            type_params: Vec::new(),
1500            custom_attrs: Vec::new(),
1501            nested: Vec::new(),
1502        }
1503    }
1504    /// Add a method.
1505    pub fn add_method(&mut self, method: CilMethod) {
1506        self.methods.push(method);
1507    }
1508    /// Add a field.
1509    pub fn add_field(&mut self, field: CilField) {
1510        self.fields.push(field);
1511    }
1512    /// Add a nested class.
1513    pub fn add_nested(&mut self, nested: CilClass) {
1514        self.nested.push(nested);
1515    }
1516    /// Get the fully qualified type name.
1517    pub fn full_name(&self) -> std::string::String {
1518        if self.namespace.is_empty() {
1519            self.name.clone()
1520        } else {
1521            format!("{}.{}", self.namespace, self.name)
1522        }
1523    }
1524    /// Find a method by name.
1525    pub fn find_method(&self, name: &str) -> Option<&CilMethod> {
1526        self.methods.iter().find(|m| m.name == name)
1527    }
1528}
1529/// A .NET assembly containing one or more classes.
1530#[derive(Debug, Clone)]
1531pub struct CilAssembly {
1532    pub name: std::string::String,
1533    pub version: (u16, u16, u16, u16),
1534    pub classes: Vec<CilClass>,
1535    pub entry_point: Option<(std::string::String, std::string::String)>,
1536    pub custom_attrs: Vec<std::string::String>,
1537    pub references: Vec<std::string::String>,
1538    pub target_runtime: std::string::String,
1539}
1540impl CilAssembly {
1541    /// Create a new assembly.
1542    pub fn new(name: impl Into<std::string::String>) -> Self {
1543        CilAssembly {
1544            name: name.into(),
1545            version: (1, 0, 0, 0),
1546            classes: Vec::new(),
1547            entry_point: None,
1548            custom_attrs: Vec::new(),
1549            references: vec!["mscorlib".to_string()],
1550            target_runtime: "v4.0.30319".to_string(),
1551        }
1552    }
1553    /// Add a class.
1554    pub fn add_class(&mut self, class: CilClass) {
1555        self.classes.push(class);
1556    }
1557    /// Find a class by full name.
1558    pub fn find_class(&self, full_name: &str) -> Option<&CilClass> {
1559        self.classes.iter().find(|c| c.full_name() == full_name)
1560    }
1561    /// Set the entry point.
1562    pub fn set_entry_point(
1563        &mut self,
1564        class_name: impl Into<std::string::String>,
1565        method_name: impl Into<std::string::String>,
1566    ) {
1567        self.entry_point = Some((class_name.into(), method_name.into()));
1568    }
1569}
1570/// A diagnostic message from a CilExt pass.
1571#[derive(Debug, Clone)]
1572pub struct CilExtDiagMsg {
1573    pub severity: CilExtDiagSeverity,
1574    pub pass: String,
1575    pub message: String,
1576}
1577impl CilExtDiagMsg {
1578    pub fn error(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1579        CilExtDiagMsg {
1580            severity: CilExtDiagSeverity::Error,
1581            pass: pass.into(),
1582            message: msg.into(),
1583        }
1584    }
1585    pub fn warning(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1586        CilExtDiagMsg {
1587            severity: CilExtDiagSeverity::Warning,
1588            pass: pass.into(),
1589            message: msg.into(),
1590        }
1591    }
1592    pub fn note(pass: impl Into<String>, msg: impl Into<String>) -> Self {
1593        CilExtDiagMsg {
1594            severity: CilExtDiagSeverity::Note,
1595            pass: pass.into(),
1596            message: msg.into(),
1597        }
1598    }
1599}
1600/// A generic key-value configuration store for CilExt.
1601#[derive(Debug, Clone, Default)]
1602pub struct CilExtConfig {
1603    pub(super) entries: std::collections::HashMap<String, String>,
1604}
1605impl CilExtConfig {
1606    pub fn new() -> Self {
1607        CilExtConfig::default()
1608    }
1609    pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
1610        self.entries.insert(key.into(), value.into());
1611    }
1612    pub fn get(&self, key: &str) -> Option<&str> {
1613        self.entries.get(key).map(|s| s.as_str())
1614    }
1615    pub fn get_bool(&self, key: &str) -> bool {
1616        matches!(self.get(key), Some("true") | Some("1") | Some("yes"))
1617    }
1618    pub fn get_int(&self, key: &str) -> Option<i64> {
1619        self.get(key)?.parse().ok()
1620    }
1621    pub fn len(&self) -> usize {
1622        self.entries.len()
1623    }
1624    pub fn is_empty(&self) -> bool {
1625        self.entries.is_empty()
1626    }
1627}
1628/// Collects CilExt diagnostics.
1629#[derive(Debug, Default)]
1630pub struct CilExtDiagCollector {
1631    pub(super) msgs: Vec<CilExtDiagMsg>,
1632}
1633impl CilExtDiagCollector {
1634    pub fn new() -> Self {
1635        CilExtDiagCollector::default()
1636    }
1637    pub fn emit(&mut self, d: CilExtDiagMsg) {
1638        self.msgs.push(d);
1639    }
1640    pub fn has_errors(&self) -> bool {
1641        self.msgs
1642            .iter()
1643            .any(|d| d.severity == CilExtDiagSeverity::Error)
1644    }
1645    pub fn errors(&self) -> Vec<&CilExtDiagMsg> {
1646        self.msgs
1647            .iter()
1648            .filter(|d| d.severity == CilExtDiagSeverity::Error)
1649            .collect()
1650    }
1651    pub fn warnings(&self) -> Vec<&CilExtDiagMsg> {
1652        self.msgs
1653            .iter()
1654            .filter(|d| d.severity == CilExtDiagSeverity::Warning)
1655            .collect()
1656    }
1657    pub fn len(&self) -> usize {
1658        self.msgs.len()
1659    }
1660    pub fn is_empty(&self) -> bool {
1661        self.msgs.is_empty()
1662    }
1663    pub fn clear(&mut self) {
1664        self.msgs.clear();
1665    }
1666}
1667#[allow(dead_code)]
1668#[derive(Debug, Clone)]
1669pub struct CILWorklist {
1670    pub(super) items: std::collections::VecDeque<u32>,
1671    pub(super) in_worklist: std::collections::HashSet<u32>,
1672}
1673impl CILWorklist {
1674    #[allow(dead_code)]
1675    pub fn new() -> Self {
1676        CILWorklist {
1677            items: std::collections::VecDeque::new(),
1678            in_worklist: std::collections::HashSet::new(),
1679        }
1680    }
1681    #[allow(dead_code)]
1682    pub fn push(&mut self, item: u32) -> bool {
1683        if self.in_worklist.insert(item) {
1684            self.items.push_back(item);
1685            true
1686        } else {
1687            false
1688        }
1689    }
1690    #[allow(dead_code)]
1691    pub fn pop(&mut self) -> Option<u32> {
1692        let item = self.items.pop_front()?;
1693        self.in_worklist.remove(&item);
1694        Some(item)
1695    }
1696    #[allow(dead_code)]
1697    pub fn is_empty(&self) -> bool {
1698        self.items.is_empty()
1699    }
1700    #[allow(dead_code)]
1701    pub fn len(&self) -> usize {
1702        self.items.len()
1703    }
1704    #[allow(dead_code)]
1705    pub fn contains(&self, item: u32) -> bool {
1706        self.in_worklist.contains(&item)
1707    }
1708}
1709/// A reference to a .NET method for use in call instructions.
1710#[derive(Debug, Clone)]
1711pub struct CilMethodRef {
1712    pub call_conv: CilCallConv,
1713    pub return_type: CilType,
1714    pub declaring_type: CilType,
1715    pub name: std::string::String,
1716    pub param_types: Vec<CilType>,
1717}
1718/// An indirect call signature.
1719#[derive(Debug, Clone)]
1720pub struct CilCallSig {
1721    pub call_conv: CilCallConv,
1722    pub return_type: CilType,
1723    pub param_types: Vec<CilType>,
1724}
1725/// Emission statistics for CilExt.
1726#[derive(Debug, Clone, Default)]
1727pub struct CilExtEmitStats {
1728    pub bytes_emitted: usize,
1729    pub items_emitted: usize,
1730    pub errors: usize,
1731    pub warnings: usize,
1732    pub elapsed_ms: u64,
1733}
1734impl CilExtEmitStats {
1735    pub fn new() -> Self {
1736        CilExtEmitStats::default()
1737    }
1738    pub fn throughput_bps(&self) -> f64 {
1739        if self.elapsed_ms == 0 {
1740            0.0
1741        } else {
1742            self.bytes_emitted as f64 / (self.elapsed_ms as f64 / 1000.0)
1743        }
1744    }
1745    pub fn is_clean(&self) -> bool {
1746        self.errors == 0
1747    }
1748}
1749#[allow(dead_code)]
1750#[derive(Debug, Clone)]
1751pub struct CILDominatorTree {
1752    pub idom: Vec<Option<u32>>,
1753    pub dom_children: Vec<Vec<u32>>,
1754    pub dom_depth: Vec<u32>,
1755}
1756impl CILDominatorTree {
1757    #[allow(dead_code)]
1758    pub fn new(size: usize) -> Self {
1759        CILDominatorTree {
1760            idom: vec![None; size],
1761            dom_children: vec![Vec::new(); size],
1762            dom_depth: vec![0; size],
1763        }
1764    }
1765    #[allow(dead_code)]
1766    pub fn set_idom(&mut self, node: usize, idom: u32) {
1767        self.idom[node] = Some(idom);
1768    }
1769    #[allow(dead_code)]
1770    pub fn dominates(&self, a: usize, b: usize) -> bool {
1771        if a == b {
1772            return true;
1773        }
1774        let mut cur = b;
1775        loop {
1776            match self.idom[cur] {
1777                Some(parent) if parent as usize == a => return true,
1778                Some(parent) if parent as usize == cur => return false,
1779                Some(parent) => cur = parent as usize,
1780                None => return false,
1781            }
1782        }
1783    }
1784    #[allow(dead_code)]
1785    pub fn depth(&self, node: usize) -> u32 {
1786        self.dom_depth.get(node).copied().unwrap_or(0)
1787    }
1788}
1789/// A field declaration in a CIL class.
1790#[derive(Debug, Clone)]
1791pub struct CilField {
1792    pub name: std::string::String,
1793    pub ty: CilType,
1794    pub is_static: bool,
1795    pub visibility: CilVisibility,
1796    pub init_value: Option<CilLiteral>,
1797}
1798/// Calling convention for CIL method references.
1799#[derive(Debug, Clone, PartialEq, Eq)]
1800pub enum CilCallConv {
1801    /// Static method
1802    Default,
1803    /// Instance method
1804    Instance,
1805    /// Generic instance method
1806    Generic(u32),
1807}
1808/// Pass-timing record for CilExt profiler.
1809#[derive(Debug, Clone)]
1810pub struct CilExtPassTiming {
1811    pub pass_name: String,
1812    pub elapsed_us: u64,
1813    pub items_processed: usize,
1814    pub bytes_before: usize,
1815    pub bytes_after: usize,
1816}
1817impl CilExtPassTiming {
1818    pub fn new(
1819        pass_name: impl Into<String>,
1820        elapsed_us: u64,
1821        items: usize,
1822        before: usize,
1823        after: usize,
1824    ) -> Self {
1825        CilExtPassTiming {
1826            pass_name: pass_name.into(),
1827            elapsed_us,
1828            items_processed: items,
1829            bytes_before: before,
1830            bytes_after: after,
1831        }
1832    }
1833    pub fn throughput_mps(&self) -> f64 {
1834        if self.elapsed_us == 0 {
1835            0.0
1836        } else {
1837            self.items_processed as f64 / (self.elapsed_us as f64 / 1_000_000.0)
1838        }
1839    }
1840    pub fn size_ratio(&self) -> f64 {
1841        if self.bytes_before == 0 {
1842            1.0
1843        } else {
1844            self.bytes_after as f64 / self.bytes_before as f64
1845        }
1846    }
1847    pub fn is_profitable(&self) -> bool {
1848        self.size_ratio() <= 1.05
1849    }
1850}
1851#[allow(dead_code)]
1852#[derive(Debug, Clone, Default)]
1853pub struct CILPassStats {
1854    pub total_runs: u32,
1855    pub successful_runs: u32,
1856    pub total_changes: u64,
1857    pub time_ms: u64,
1858    pub iterations_used: u32,
1859}
1860impl CILPassStats {
1861    #[allow(dead_code)]
1862    pub fn new() -> Self {
1863        Self::default()
1864    }
1865    #[allow(dead_code)]
1866    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1867        self.total_runs += 1;
1868        self.successful_runs += 1;
1869        self.total_changes += changes;
1870        self.time_ms += time_ms;
1871        self.iterations_used = iterations;
1872    }
1873    #[allow(dead_code)]
1874    pub fn average_changes_per_run(&self) -> f64 {
1875        if self.total_runs == 0 {
1876            return 0.0;
1877        }
1878        self.total_changes as f64 / self.total_runs as f64
1879    }
1880    #[allow(dead_code)]
1881    pub fn success_rate(&self) -> f64 {
1882        if self.total_runs == 0 {
1883            return 0.0;
1884        }
1885        self.successful_runs as f64 / self.total_runs as f64
1886    }
1887    #[allow(dead_code)]
1888    pub fn format_summary(&self) -> String {
1889        format!(
1890            "Runs: {}/{}, Changes: {}, Time: {}ms",
1891            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1892        )
1893    }
1894}