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::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(¶ms[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 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 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 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 #[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(¶m.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 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(¶m.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 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 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 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#[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#[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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1119pub enum CilType {
1120 Void,
1122 Bool,
1124 Int8,
1126 Int16,
1128 Int32,
1130 Int64,
1132 UInt8,
1134 UInt16,
1136 UInt32,
1138 UInt64,
1140 Float32,
1142 Float64,
1144 Char,
1146 String,
1148 Object,
1150 Class {
1152 assembly: Option<std::string::String>,
1153 namespace: std::string::String,
1154 name: std::string::String,
1155 },
1156 ValueType {
1158 assembly: Option<std::string::String>,
1159 namespace: std::string::String,
1160 name: std::string::String,
1161 },
1162 Array(Box<CilType>),
1164 MdArray(Box<CilType>, u32),
1166 Ptr(Box<CilType>),
1168 ByRef(Box<CilType>),
1170 Generic(Box<CilType>, Vec<CilType>),
1172 GenericParam(u32),
1174 GenericMethodParam(u32),
1176 NativeInt,
1178 NativeUInt,
1180}
1181impl CilType {
1182 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 pub fn is_reference_type(&self) -> bool {
1205 !self.is_value_type()
1206 }
1207 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 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 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 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#[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#[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#[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 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 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 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 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 pub fn emit(&mut self, instr: CilInstr) {
1393 self.instructions.push(instr);
1394 }
1395 pub fn emit_label(&mut self, label: impl Into<std::string::String>) {
1397 self.instructions.push(CilInstr::Label(label.into()));
1398 }
1399}
1400#[derive(Debug, Clone, PartialEq, Eq)]
1402pub enum CilVisibility {
1403 Private,
1404 Assembly,
1405 Family,
1406 Public,
1407}
1408#[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#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1430pub enum CilExtDiagSeverity {
1431 Note,
1432 Warning,
1433 Error,
1434}
1435#[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#[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 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 pub fn add_method(&mut self, method: CilMethod) {
1506 self.methods.push(method);
1507 }
1508 pub fn add_field(&mut self, field: CilField) {
1510 self.fields.push(field);
1511 }
1512 pub fn add_nested(&mut self, nested: CilClass) {
1514 self.nested.push(nested);
1515 }
1516 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 pub fn find_method(&self, name: &str) -> Option<&CilMethod> {
1526 self.methods.iter().find(|m| m.name == name)
1527 }
1528}
1529#[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 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 pub fn add_class(&mut self, class: CilClass) {
1555 self.classes.push(class);
1556 }
1557 pub fn find_class(&self, full_name: &str) -> Option<&CilClass> {
1559 self.classes.iter().find(|c| c.full_name() == full_name)
1560 }
1561 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#[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#[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#[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#[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#[derive(Debug, Clone)]
1720pub struct CilCallSig {
1721 pub call_conv: CilCallConv,
1722 pub return_type: CilType,
1723 pub param_types: Vec<CilType>,
1724}
1725#[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#[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#[derive(Debug, Clone, PartialEq, Eq)]
1800pub enum CilCallConv {
1801 Default,
1803 Instance,
1805 Generic(u32),
1807}
1808#[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}