1use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::functions::*;
9use std::collections::{HashSet, VecDeque};
10
11#[derive(Debug, Clone)]
13pub struct CilFieldRef {
14 pub field_type: CilType,
15 pub declaring_type: CilType,
16 pub name: std::string::String,
17}
18#[derive(Debug, Clone)]
22pub enum CilInstr {
23 Nop,
25 LdlocS(u16),
27 StlocS(u16),
29 LdlocaS(u16),
31 LdargS(u16),
33 StargS(u16),
35 LdargaS(u16),
37 LdcI4(i32),
39 LdcI4S(i8),
41 LdcI4Small(i32),
43 LdcI8(i64),
45 LdcR4(f32),
47 LdcR8(f64),
49 Ldnull,
51 Ldstr(std::string::String),
53 Ldsflda(CilFieldRef),
55 Ldsfld(CilFieldRef),
57 Stsfld(CilFieldRef),
59 Add,
61 AddOvf,
63 Sub,
65 SubOvf,
67 Mul,
69 MulOvf,
71 Div,
73 DivUn,
75 Rem,
77 RemUn,
79 Neg,
81 And,
83 Or,
85 Xor,
87 Not,
89 Shl,
91 Shr,
93 ShrUn,
95 Ceq,
97 Cgt,
99 CgtUn,
101 Clt,
103 CltUn,
105 Br(std::string::String),
107 Brfalse(std::string::String),
109 Brtrue(std::string::String),
111 Beq(std::string::String),
113 BneUn(std::string::String),
115 Blt(std::string::String),
117 Bgt(std::string::String),
119 Ble(std::string::String),
121 Bge(std::string::String),
123 Switch(Vec<std::string::String>),
125 Ret,
127 Throw,
129 Rethrow,
131 Label(std::string::String),
133 Call(CilMethodRef),
135 Callvirt(CilMethodRef),
137 TailCall(CilMethodRef),
139 Calli(CilCallSig),
141 Ldftn(CilMethodRef),
143 Ldvirtftn(CilMethodRef),
145 Newobj(CilMethodRef),
147 Ldobj(CilType),
149 Stobj(CilType),
151 Ldfld(CilFieldRef),
153 Stfld(CilFieldRef),
155 Ldflda(CilFieldRef),
157 Box_(CilType),
159 Unbox(CilType),
161 UnboxAny(CilType),
163 Isinst(CilType),
165 Castclass(CilType),
167 Initobj(CilType),
169 Sizeof(CilType),
171 Ldtoken(CilType),
173 Newarr(CilType),
175 Ldlen,
177 Ldelem(CilType),
179 Stelem(CilType),
181 Ldelema(CilType),
183 Dup,
185 Pop,
187 ConvI4,
189 ConvI8,
191 ConvR4,
193 ConvR8,
195 ConvU4,
197 ConvU8,
199 LdindI4,
201 StindI4,
203 Localloc,
205 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#[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#[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#[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#[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}
452pub 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 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 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 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(¶ms[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 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 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 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 #[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(¶m.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 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(¶m.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 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 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 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#[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#[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1117pub enum CilType {
1118 Void,
1120 Bool,
1122 Int8,
1124 Int16,
1126 Int32,
1128 Int64,
1130 UInt8,
1132 UInt16,
1134 UInt32,
1136 UInt64,
1138 Float32,
1140 Float64,
1142 Char,
1144 String,
1146 Object,
1148 Class {
1150 assembly: Option<std::string::String>,
1151 namespace: std::string::String,
1152 name: std::string::String,
1153 },
1154 ValueType {
1156 assembly: Option<std::string::String>,
1157 namespace: std::string::String,
1158 name: std::string::String,
1159 },
1160 Array(Box<CilType>),
1162 MdArray(Box<CilType>, u32),
1164 Ptr(Box<CilType>),
1166 ByRef(Box<CilType>),
1168 Generic(Box<CilType>, Vec<CilType>),
1170 GenericParam(u32),
1172 GenericMethodParam(u32),
1174 NativeInt,
1176 NativeUInt,
1178}
1179impl CilType {
1180 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 pub fn is_reference_type(&self) -> bool {
1203 !self.is_value_type()
1204 }
1205 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 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 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 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#[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#[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#[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 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 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 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 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 pub fn emit(&mut self, instr: CilInstr) {
1391 self.instructions.push(instr);
1392 }
1393 pub fn emit_label(&mut self, label: impl Into<std::string::String>) {
1395 self.instructions.push(CilInstr::Label(label.into()));
1396 }
1397}
1398#[derive(Debug, Clone, PartialEq, Eq)]
1400pub enum CilVisibility {
1401 Private,
1402 Assembly,
1403 Family,
1404 Public,
1405}
1406#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1428pub enum CilExtDiagSeverity {
1429 Note,
1430 Warning,
1431 Error,
1432}
1433#[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#[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 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 pub fn add_method(&mut self, method: CilMethod) {
1504 self.methods.push(method);
1505 }
1506 pub fn add_field(&mut self, field: CilField) {
1508 self.fields.push(field);
1509 }
1510 pub fn add_nested(&mut self, nested: CilClass) {
1512 self.nested.push(nested);
1513 }
1514 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 pub fn find_method(&self, name: &str) -> Option<&CilMethod> {
1524 self.methods.iter().find(|m| m.name == name)
1525 }
1526}
1527#[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 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 pub fn add_class(&mut self, class: CilClass) {
1553 self.classes.push(class);
1554 }
1555 pub fn find_class(&self, full_name: &str) -> Option<&CilClass> {
1557 self.classes.iter().find(|c| c.full_name() == full_name)
1558 }
1559 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#[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#[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#[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#[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#[derive(Debug, Clone)]
1718pub struct CilCallSig {
1719 pub call_conv: CilCallConv,
1720 pub return_type: CilType,
1721 pub param_types: Vec<CilType>,
1722}
1723#[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#[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#[derive(Debug, Clone, PartialEq, Eq)]
1798pub enum CilCallConv {
1799 Default,
1801 Instance,
1803 Generic(u32),
1805}
1806#[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}