1use crate::lcnf::*;
6use std::collections::{BTreeMap, HashMap};
7
8use super::functions::*;
9use std::collections::{HashSet, VecDeque};
10
11#[allow(dead_code)]
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum FfiPlatformTarget {
15 Linux,
16 Windows,
17 MacOS,
18 FreeBSD,
19 Wasm32,
20 Wasm64,
21 Android,
22 Ios,
23 Universal,
24}
25#[allow(dead_code)]
27#[derive(Debug, Default)]
28pub struct FfiHeaderBuilder {
29 pub guard: Option<String>,
30 pub includes: Vec<String>,
31 pub typedefs: Vec<FfiTypedef>,
32 pub structs: Vec<FfiStructDef>,
33 pub enums: Vec<FfiEnumDef>,
34 pub functions: Vec<FfiFuncSignature>,
35 pub constants: Vec<FfiConst>,
36 pub callbacks: Vec<FfiCallbackType>,
37}
38#[allow(dead_code)]
39impl FfiHeaderBuilder {
40 pub fn new() -> Self {
41 Self::default()
42 }
43 pub fn with_guard(mut self, guard: &str) -> Self {
44 self.guard = Some(guard.to_string());
45 self
46 }
47 pub fn add_include(&mut self, inc: &str) {
48 self.includes.push(inc.to_string());
49 }
50 pub fn add_typedef(&mut self, td: FfiTypedef) {
51 self.typedefs.push(td);
52 }
53 pub fn add_struct(&mut self, s: FfiStructDef) {
54 self.structs.push(s);
55 }
56 pub fn add_enum(&mut self, e: FfiEnumDef) {
57 self.enums.push(e);
58 }
59 pub fn add_function(&mut self, f: FfiFuncSignature) {
60 self.functions.push(f);
61 }
62 pub fn add_const(&mut self, c: FfiConst) {
63 self.constants.push(c);
64 }
65 pub fn add_callback(&mut self, cb: FfiCallbackType) {
66 self.callbacks.push(cb);
67 }
68 pub fn build(&self) -> String {
69 let mut out = String::new();
70 if let Some(guard) = &self.guard {
71 out.push_str(&format!("#ifndef {}\n#define {}\n\n", guard, guard));
72 }
73 for inc in &self.includes {
74 if inc.starts_with('<') {
75 out.push_str(&format!("#include {}\n", inc));
76 } else {
77 out.push_str(&format!("#include \"{}\"\n", inc));
78 }
79 }
80 if !self.includes.is_empty() {
81 out.push('\n');
82 }
83 for td in &self.typedefs {
84 out.push_str(&format!("{}\n", td));
85 }
86 for s in &self.structs {
87 out.push_str(&format!("{}\n\n", s));
88 }
89 for e in &self.enums {
90 out.push_str(&format!("{}\n\n", e));
91 }
92 for cb in &self.callbacks {
93 out.push_str(&format!("{};\n", cb));
94 }
95 if !self.callbacks.is_empty() {
96 out.push('\n');
97 }
98 for c in &self.constants {
99 out.push_str(&format!("{}\n", c));
100 }
101 if !self.constants.is_empty() {
102 out.push('\n');
103 }
104 for func in &self.functions {
105 out.push_str(&format!("{};\n", func));
106 }
107 if let Some(guard) = &self.guard {
108 out.push_str(&format!("\n#endif /* {} */\n", guard));
109 }
110 out
111 }
112}
113#[derive(Debug, Clone)]
115pub struct FfiExtDiagMsg {
116 pub severity: FfiExtDiagSeverity,
117 pub pass: String,
118 pub message: String,
119}
120impl FfiExtDiagMsg {
121 pub fn error(pass: impl Into<String>, msg: impl Into<String>) -> Self {
122 FfiExtDiagMsg {
123 severity: FfiExtDiagSeverity::Error,
124 pass: pass.into(),
125 message: msg.into(),
126 }
127 }
128 pub fn warning(pass: impl Into<String>, msg: impl Into<String>) -> Self {
129 FfiExtDiagMsg {
130 severity: FfiExtDiagSeverity::Warning,
131 pass: pass.into(),
132 message: msg.into(),
133 }
134 }
135 pub fn note(pass: impl Into<String>, msg: impl Into<String>) -> Self {
136 FfiExtDiagMsg {
137 severity: FfiExtDiagSeverity::Note,
138 pass: pass.into(),
139 message: msg.into(),
140 }
141 }
142}
143#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
145pub struct FfiExtVersion {
146 pub major: u32,
147 pub minor: u32,
148 pub patch: u32,
149 pub pre: Option<String>,
150}
151impl FfiExtVersion {
152 pub fn new(major: u32, minor: u32, patch: u32) -> Self {
153 FfiExtVersion {
154 major,
155 minor,
156 patch,
157 pre: None,
158 }
159 }
160 pub fn with_pre(mut self, pre: impl Into<String>) -> Self {
161 self.pre = Some(pre.into());
162 self
163 }
164 pub fn is_stable(&self) -> bool {
165 self.pre.is_none()
166 }
167 pub fn is_compatible_with(&self, other: &FfiExtVersion) -> bool {
168 self.major == other.major && self.minor >= other.minor
169 }
170}
171#[derive(Debug, Default)]
173pub struct FfiExtIdGen {
174 pub(super) next: u32,
175}
176impl FfiExtIdGen {
177 pub fn new() -> Self {
178 FfiExtIdGen::default()
179 }
180 pub fn next_id(&mut self) -> u32 {
181 let id = self.next;
182 self.next += 1;
183 id
184 }
185 pub fn peek_next(&self) -> u32 {
186 self.next
187 }
188 pub fn reset(&mut self) {
189 self.next = 0;
190 }
191 pub fn skip(&mut self, n: u32) {
192 self.next += n;
193 }
194}
195#[allow(dead_code)]
196#[derive(Debug, Clone, Default)]
197pub struct FFIPassStats {
198 pub total_runs: u32,
199 pub successful_runs: u32,
200 pub total_changes: u64,
201 pub time_ms: u64,
202 pub iterations_used: u32,
203}
204impl FFIPassStats {
205 #[allow(dead_code)]
206 pub fn new() -> Self {
207 Self::default()
208 }
209 #[allow(dead_code)]
210 pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
211 self.total_runs += 1;
212 self.successful_runs += 1;
213 self.total_changes += changes;
214 self.time_ms += time_ms;
215 self.iterations_used = iterations;
216 }
217 #[allow(dead_code)]
218 pub fn average_changes_per_run(&self) -> f64 {
219 if self.total_runs == 0 {
220 return 0.0;
221 }
222 self.total_changes as f64 / self.total_runs as f64
223 }
224 #[allow(dead_code)]
225 pub fn success_rate(&self) -> f64 {
226 if self.total_runs == 0 {
227 return 0.0;
228 }
229 self.successful_runs as f64 / self.total_runs as f64
230 }
231 #[allow(dead_code)]
232 pub fn format_summary(&self) -> String {
233 format!(
234 "Runs: {}/{}, Changes: {}, Time: {}ms",
235 self.successful_runs, self.total_runs, self.total_changes, self.time_ms
236 )
237 }
238}
239#[allow(dead_code)]
241#[derive(Debug, Clone)]
242pub struct FfiVerifyResult {
243 pub ok: bool,
244 pub errors: Vec<String>,
245 pub warnings: Vec<String>,
246}
247#[derive(Debug, Clone)]
249pub struct FfiDecl {
250 pub name: String,
252 pub extern_name: String,
254 pub params: Vec<(String, FfiNativeType)>,
256 pub ret_type: FfiNativeType,
258 pub calling_conv: CallingConvention,
260 pub is_unsafe: bool,
262}
263#[allow(dead_code)]
265#[derive(Debug, Default)]
266pub struct FfiPythonCffi {
267 pub ffi_defs: Vec<String>,
268 pub lib_name: String,
269 pub header_file: String,
270}
271#[allow(dead_code)]
272impl FfiPythonCffi {
273 pub fn new(lib: &str, header: &str) -> Self {
274 Self {
275 ffi_defs: Vec::new(),
276 lib_name: lib.to_string(),
277 header_file: header.to_string(),
278 }
279 }
280 pub fn add_def(&mut self, def: &str) {
281 self.ffi_defs.push(def.to_string());
282 }
283 pub fn emit(&self) -> String {
284 let defs = self.ffi_defs.join("\n");
285 format!(
286 "from cffi import FFI\nffi = FFI()\nffi.cdef(\"\"\"\n{}\n\"\"\")\nlib = ffi.dlopen(\"lib{}.so\")\n",
287 defs, self.lib_name
288 )
289 }
290}
291#[allow(dead_code)]
292pub struct FFIPassRegistry {
293 pub(super) configs: Vec<FFIPassConfig>,
294 pub(super) stats: std::collections::HashMap<String, FFIPassStats>,
295}
296impl FFIPassRegistry {
297 #[allow(dead_code)]
298 pub fn new() -> Self {
299 FFIPassRegistry {
300 configs: Vec::new(),
301 stats: std::collections::HashMap::new(),
302 }
303 }
304 #[allow(dead_code)]
305 pub fn register(&mut self, config: FFIPassConfig) {
306 self.stats
307 .insert(config.pass_name.clone(), FFIPassStats::new());
308 self.configs.push(config);
309 }
310 #[allow(dead_code)]
311 pub fn enabled_passes(&self) -> Vec<&FFIPassConfig> {
312 self.configs.iter().filter(|c| c.enabled).collect()
313 }
314 #[allow(dead_code)]
315 pub fn get_stats(&self, name: &str) -> Option<&FFIPassStats> {
316 self.stats.get(name)
317 }
318 #[allow(dead_code)]
319 pub fn total_passes(&self) -> usize {
320 self.configs.len()
321 }
322 #[allow(dead_code)]
323 pub fn enabled_count(&self) -> usize {
324 self.enabled_passes().len()
325 }
326 #[allow(dead_code)]
327 pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
328 if let Some(stats) = self.stats.get_mut(name) {
329 stats.record_run(changes, time_ms, iter);
330 }
331 }
332}
333#[allow(dead_code)]
335#[derive(Debug, Clone, PartialEq, Eq)]
336pub enum FfiParamAttr {
337 In,
338 Out,
339 InOut,
340 Const,
341 Volatile,
342 Restrict,
343 NullTerminated,
344 Nonnull,
345 Nullable,
346 Retain,
347 Escaping,
348}
349#[allow(dead_code)]
350#[derive(Debug, Clone, PartialEq, Eq)]
351pub enum FfiRustBindingKind {
352 Function,
353 Struct,
354 Enum,
355 Type,
356 Const,
357}
358#[allow(dead_code)]
360#[derive(Debug, Default)]
361pub struct FfiZigBinding {
362 pub pub_fns: Vec<String>,
363 pub types: Vec<String>,
364 pub lib_path: String,
365}
366#[allow(dead_code)]
367impl FfiZigBinding {
368 pub fn new(lib_path: &str) -> Self {
369 Self {
370 pub_fns: Vec::new(),
371 types: Vec::new(),
372 lib_path: lib_path.to_string(),
373 }
374 }
375 pub fn add_fn(&mut self, f: &str) {
376 self.pub_fns.push(f.to_string());
377 }
378 pub fn add_type(&mut self, t: &str) {
379 self.types.push(t.to_string());
380 }
381 pub fn emit(&self) -> String {
382 let mut out = format!("const c = @cImport(@cInclude(\"{}\"));\n\n", self.lib_path);
383 for t in &self.types {
384 out.push_str(&format!("pub const {} = c.{};\n", t, t));
385 }
386 for f in &self.pub_fns {
387 out.push_str(&format!("pub const {} = c.{};\n", f, f));
388 }
389 out
390 }
391}
392#[derive(Debug, Clone)]
394pub struct FfiOutput {
395 pub rust_bindings: String,
397 pub c_header: String,
399 pub wrapper_fns: Vec<String>,
401}
402impl FfiOutput {
403 pub(super) fn new() -> Self {
404 FfiOutput {
405 rust_bindings: String::new(),
406 c_header: String::new(),
407 wrapper_fns: Vec::new(),
408 }
409 }
410}
411#[allow(dead_code)]
412#[derive(Debug, Clone)]
413pub struct FFICacheEntry {
414 pub key: String,
415 pub data: Vec<u8>,
416 pub timestamp: u64,
417 pub valid: bool,
418}
419#[derive(Debug, Clone, Default)]
421pub struct FfiExtFeatures {
422 pub(super) flags: std::collections::HashSet<String>,
423}
424impl FfiExtFeatures {
425 pub fn new() -> Self {
426 FfiExtFeatures::default()
427 }
428 pub fn enable(&mut self, flag: impl Into<String>) {
429 self.flags.insert(flag.into());
430 }
431 pub fn disable(&mut self, flag: &str) {
432 self.flags.remove(flag);
433 }
434 pub fn is_enabled(&self, flag: &str) -> bool {
435 self.flags.contains(flag)
436 }
437 pub fn len(&self) -> usize {
438 self.flags.len()
439 }
440 pub fn is_empty(&self) -> bool {
441 self.flags.is_empty()
442 }
443 pub fn union(&self, other: &FfiExtFeatures) -> FfiExtFeatures {
444 FfiExtFeatures {
445 flags: self.flags.union(&other.flags).cloned().collect(),
446 }
447 }
448 pub fn intersection(&self, other: &FfiExtFeatures) -> FfiExtFeatures {
449 FfiExtFeatures {
450 flags: self.flags.intersection(&other.flags).cloned().collect(),
451 }
452 }
453}
454#[derive(Debug, Clone, PartialEq, Eq, Hash)]
456pub enum FfiNativeType {
457 Void,
459 I8,
461 I16,
463 I32,
465 I64,
467 U8,
469 U16,
471 U32,
473 U64,
475 F32,
477 F64,
479 Bool,
481 SizeT,
483 CStr,
485 Ptr(Box<FfiNativeType>),
487 OpaquePtr,
489 LeanObject,
491 Struct(String, Vec<(String, FfiNativeType)>),
493 FnPtr(Vec<FfiNativeType>, Box<FfiNativeType>),
495}
496impl FfiNativeType {
497 pub(super) fn to_rust_type(&self) -> String {
499 match self {
500 FfiNativeType::Void => "()".to_string(),
501 FfiNativeType::I8 => "i8".to_string(),
502 FfiNativeType::I16 => "i16".to_string(),
503 FfiNativeType::I32 => "i32".to_string(),
504 FfiNativeType::I64 => "i64".to_string(),
505 FfiNativeType::U8 => "u8".to_string(),
506 FfiNativeType::U16 => "u16".to_string(),
507 FfiNativeType::U32 => "u32".to_string(),
508 FfiNativeType::U64 => "u64".to_string(),
509 FfiNativeType::F32 => "f32".to_string(),
510 FfiNativeType::F64 => "f64".to_string(),
511 FfiNativeType::Bool => "bool".to_string(),
512 FfiNativeType::SizeT => "usize".to_string(),
513 FfiNativeType::CStr => "*const std::os::raw::c_char".to_string(),
514 FfiNativeType::Ptr(inner) => format!("*mut {}", inner.to_rust_type()),
515 FfiNativeType::OpaquePtr => "*mut std::os::raw::c_void".to_string(),
516 FfiNativeType::LeanObject => "*mut LeanObject".to_string(),
517 FfiNativeType::Struct(name, _) => name.clone(),
518 FfiNativeType::FnPtr(params, ret) => {
519 let param_strs: Vec<String> = params.iter().map(|p| p.to_rust_type()).collect();
520 format!(
521 "Option<unsafe extern \"C\" fn({}) -> {}>",
522 param_strs.join(", "),
523 ret.to_rust_type()
524 )
525 }
526 }
527 }
528 pub(super) fn size_bytes(&self) -> usize {
530 match self {
531 FfiNativeType::Void => 0,
532 FfiNativeType::I8 | FfiNativeType::U8 | FfiNativeType::Bool => 1,
533 FfiNativeType::I16 | FfiNativeType::U16 => 2,
534 FfiNativeType::I32 | FfiNativeType::U32 | FfiNativeType::F32 => 4,
535 FfiNativeType::I64
536 | FfiNativeType::U64
537 | FfiNativeType::F64
538 | FfiNativeType::SizeT
539 | FfiNativeType::CStr
540 | FfiNativeType::Ptr(_)
541 | FfiNativeType::OpaquePtr
542 | FfiNativeType::LeanObject
543 | FfiNativeType::FnPtr(_, _) => 8,
544 FfiNativeType::Struct(_, fields) => fields.iter().map(|(_, ty)| ty.size_bytes()).sum(),
545 }
546 }
547}
548#[derive(Debug, Default)]
550pub struct FfiExtNameScope {
551 pub(super) declared: std::collections::HashSet<String>,
552 pub(super) depth: usize,
553 pub(super) parent: Option<Box<FfiExtNameScope>>,
554}
555impl FfiExtNameScope {
556 pub fn new() -> Self {
557 FfiExtNameScope::default()
558 }
559 pub fn declare(&mut self, name: impl Into<String>) -> bool {
560 self.declared.insert(name.into())
561 }
562 pub fn is_declared(&self, name: &str) -> bool {
563 self.declared.contains(name)
564 }
565 pub fn push_scope(self) -> Self {
566 FfiExtNameScope {
567 declared: std::collections::HashSet::new(),
568 depth: self.depth + 1,
569 parent: Some(Box::new(self)),
570 }
571 }
572 pub fn pop_scope(self) -> Self {
573 *self.parent.unwrap_or_default()
574 }
575 pub fn depth(&self) -> usize {
576 self.depth
577 }
578 pub fn len(&self) -> usize {
579 self.declared.len()
580 }
581}
582#[allow(dead_code)]
584#[derive(Debug, Default, Clone)]
585pub struct FfiBridgeStatsV2 {
586 pub functions_bridged: usize,
587 pub structs_bridged: usize,
588 pub enums_bridged: usize,
589 pub typedefs_emitted: usize,
590 pub callbacks_emitted: usize,
591 pub constants_emitted: usize,
592 pub headers_generated: usize,
593 pub bytes_total: usize,
594}
595#[allow(dead_code)]
596#[derive(Debug, Clone, PartialEq)]
597pub enum FFIPassPhase {
598 Analysis,
599 Transformation,
600 Verification,
601 Cleanup,
602}
603impl FFIPassPhase {
604 #[allow(dead_code)]
605 pub fn name(&self) -> &str {
606 match self {
607 FFIPassPhase::Analysis => "analysis",
608 FFIPassPhase::Transformation => "transformation",
609 FFIPassPhase::Verification => "verification",
610 FFIPassPhase::Cleanup => "cleanup",
611 }
612 }
613 #[allow(dead_code)]
614 pub fn is_modifying(&self) -> bool {
615 matches!(self, FFIPassPhase::Transformation | FFIPassPhase::Cleanup)
616 }
617}
618#[allow(dead_code)]
620#[derive(Debug, Clone, PartialEq, Eq)]
621pub enum FfiNullTermPolicy {
622 Always,
623 Never,
624 WhenNonNull,
625}
626#[allow(dead_code)]
628#[derive(Debug, Clone)]
629pub struct FfiBufferParam {
630 pub data: FfiFuncParam,
631 pub size: FfiSizeHint,
632 pub null_term: FfiNullTermPolicy,
633}
634#[derive(Debug, Clone)]
636pub struct FfiMarshalInfo {
637 pub native_type: FfiNativeType,
639 pub to_native: String,
641 pub from_native: String,
643 pub is_trivial: bool,
645 pub needs_free: bool,
647}
648impl FfiMarshalInfo {
649 pub(super) fn trivial(native_type: FfiNativeType) -> Self {
651 FfiMarshalInfo {
652 native_type,
653 to_native: String::new(),
654 from_native: String::new(),
655 is_trivial: true,
656 needs_free: false,
657 }
658 }
659 pub(super) fn with_conversion(
661 native_type: FfiNativeType,
662 to_native: &str,
663 from_native: &str,
664 ) -> Self {
665 FfiMarshalInfo {
666 native_type,
667 to_native: to_native.to_string(),
668 from_native: from_native.to_string(),
669 is_trivial: false,
670 needs_free: false,
671 }
672 }
673}
674#[allow(dead_code)]
676#[derive(Debug, Clone, PartialEq, Eq)]
677pub enum FfiCallingConv {
678 C,
679 StdCall,
680 FastCall,
681 ThisCall,
682 VectorCall,
683 Win64,
684 SysV64,
685 Swift,
686 Rust,
687 Custom(String),
688}
689#[allow(dead_code)]
691#[derive(Debug, Default)]
692pub struct FfiExtProfilerV2 {
693 pub timings: Vec<(String, u64)>,
694}
695#[allow(dead_code)]
696impl FfiExtProfilerV2 {
697 pub fn new() -> Self {
698 Self::default()
699 }
700 pub fn record(&mut self, pass: &str, us: u64) {
701 self.timings.push((pass.to_string(), us));
702 }
703 pub fn total_us(&self) -> u64 {
704 self.timings.iter().map(|(_, t)| *t).sum()
705 }
706}
707#[allow(dead_code)]
708#[derive(Debug, Clone)]
709pub struct FFIDominatorTree {
710 pub idom: Vec<Option<u32>>,
711 pub dom_children: Vec<Vec<u32>>,
712 pub dom_depth: Vec<u32>,
713}
714impl FFIDominatorTree {
715 #[allow(dead_code)]
716 pub fn new(size: usize) -> Self {
717 FFIDominatorTree {
718 idom: vec![None; size],
719 dom_children: vec![Vec::new(); size],
720 dom_depth: vec![0; size],
721 }
722 }
723 #[allow(dead_code)]
724 pub fn set_idom(&mut self, node: usize, idom: u32) {
725 self.idom[node] = Some(idom);
726 }
727 #[allow(dead_code)]
728 pub fn dominates(&self, a: usize, b: usize) -> bool {
729 if a == b {
730 return true;
731 }
732 let mut cur = b;
733 loop {
734 match self.idom[cur] {
735 Some(parent) if parent as usize == a => return true,
736 Some(parent) if parent as usize == cur => return false,
737 Some(parent) => cur = parent as usize,
738 None => return false,
739 }
740 }
741 }
742 #[allow(dead_code)]
743 pub fn depth(&self, node: usize) -> u32 {
744 self.dom_depth.get(node).copied().unwrap_or(0)
745 }
746}
747#[allow(dead_code)]
749#[derive(Debug, Default)]
750pub struct FfiErrorTable {
751 pub codes: Vec<FfiErrorCode>,
752}
753#[allow(dead_code)]
754impl FfiErrorTable {
755 pub fn new() -> Self {
756 Self::default()
757 }
758 pub fn add(&mut self, code: FfiErrorCode) {
759 self.codes.push(code);
760 }
761 pub fn lookup(&self, value: i32) -> Option<&FfiErrorCode> {
762 self.codes.iter().find(|c| c.value == value)
763 }
764 pub fn emit_enum(&self, name: &str) -> String {
765 let mut out = format!("enum {} {{\n", name);
766 for c in &self.codes {
767 out.push_str(&format!(" {} = {},\n", c.name, c.value));
768 }
769 out.push_str("};\n");
770 out
771 }
772}
773#[allow(dead_code)]
774#[derive(Debug, Clone)]
775pub struct FFIPassConfig {
776 pub phase: FFIPassPhase,
777 pub enabled: bool,
778 pub max_iterations: u32,
779 pub debug_output: bool,
780 pub pass_name: String,
781}
782impl FFIPassConfig {
783 #[allow(dead_code)]
784 pub fn new(name: impl Into<String>, phase: FFIPassPhase) -> Self {
785 FFIPassConfig {
786 phase,
787 enabled: true,
788 max_iterations: 10,
789 debug_output: false,
790 pass_name: name.into(),
791 }
792 }
793 #[allow(dead_code)]
794 pub fn disabled(mut self) -> Self {
795 self.enabled = false;
796 self
797 }
798 #[allow(dead_code)]
799 pub fn with_debug(mut self) -> Self {
800 self.debug_output = true;
801 self
802 }
803 #[allow(dead_code)]
804 pub fn max_iter(mut self, n: u32) -> Self {
805 self.max_iterations = n;
806 self
807 }
808}
809#[allow(dead_code)]
811#[derive(Debug, Clone)]
812pub struct FfiEnumDef {
813 pub name: String,
814 pub variants: Vec<(String, Option<i64>)>,
815 pub underlying_type: Option<String>,
816}
817#[derive(Debug, Default)]
819pub struct FfiExtDiagCollector {
820 pub(super) msgs: Vec<FfiExtDiagMsg>,
821}
822impl FfiExtDiagCollector {
823 pub fn new() -> Self {
824 FfiExtDiagCollector::default()
825 }
826 pub fn emit(&mut self, d: FfiExtDiagMsg) {
827 self.msgs.push(d);
828 }
829 pub fn has_errors(&self) -> bool {
830 self.msgs
831 .iter()
832 .any(|d| d.severity == FfiExtDiagSeverity::Error)
833 }
834 pub fn errors(&self) -> Vec<&FfiExtDiagMsg> {
835 self.msgs
836 .iter()
837 .filter(|d| d.severity == FfiExtDiagSeverity::Error)
838 .collect()
839 }
840 pub fn warnings(&self) -> Vec<&FfiExtDiagMsg> {
841 self.msgs
842 .iter()
843 .filter(|d| d.severity == FfiExtDiagSeverity::Warning)
844 .collect()
845 }
846 pub fn len(&self) -> usize {
847 self.msgs.len()
848 }
849 pub fn is_empty(&self) -> bool {
850 self.msgs.is_empty()
851 }
852 pub fn clear(&mut self) {
853 self.msgs.clear();
854 }
855}
856#[derive(Debug)]
858pub struct FfiExtEventLog {
859 pub(super) entries: std::collections::VecDeque<String>,
860 pub(super) capacity: usize,
861}
862impl FfiExtEventLog {
863 pub fn new(capacity: usize) -> Self {
864 FfiExtEventLog {
865 entries: std::collections::VecDeque::with_capacity(capacity),
866 capacity,
867 }
868 }
869 pub fn push(&mut self, event: impl Into<String>) {
870 if self.entries.len() >= self.capacity {
871 self.entries.pop_front();
872 }
873 self.entries.push_back(event.into());
874 }
875 pub fn iter(&self) -> impl Iterator<Item = &String> {
876 self.entries.iter()
877 }
878 pub fn len(&self) -> usize {
879 self.entries.len()
880 }
881 pub fn is_empty(&self) -> bool {
882 self.entries.is_empty()
883 }
884 pub fn capacity(&self) -> usize {
885 self.capacity
886 }
887 pub fn clear(&mut self) {
888 self.entries.clear();
889 }
890}
891#[derive(Debug, Clone)]
893pub struct FfiExtPassTiming {
894 pub pass_name: String,
895 pub elapsed_us: u64,
896 pub items_processed: usize,
897 pub bytes_before: usize,
898 pub bytes_after: usize,
899}
900impl FfiExtPassTiming {
901 pub fn new(
902 pass_name: impl Into<String>,
903 elapsed_us: u64,
904 items: usize,
905 before: usize,
906 after: usize,
907 ) -> Self {
908 FfiExtPassTiming {
909 pass_name: pass_name.into(),
910 elapsed_us,
911 items_processed: items,
912 bytes_before: before,
913 bytes_after: after,
914 }
915 }
916 pub fn throughput_mps(&self) -> f64 {
917 if self.elapsed_us == 0 {
918 0.0
919 } else {
920 self.items_processed as f64 / (self.elapsed_us as f64 / 1_000_000.0)
921 }
922 }
923 pub fn size_ratio(&self) -> f64 {
924 if self.bytes_before == 0 {
925 1.0
926 } else {
927 self.bytes_after as f64 / self.bytes_before as f64
928 }
929 }
930 pub fn is_profitable(&self) -> bool {
931 self.size_ratio() <= 1.05
932 }
933}
934#[derive(Debug, Clone, Default)]
936pub struct FfiTypeMap {
937 pub(super) lcnf_to_native: HashMap<String, FfiNativeType>,
939 pub(super) native_to_lcnf: HashMap<String, LcnfType>,
941 pub(super) custom_marshal: HashMap<String, FfiMarshalInfo>,
943}
944impl FfiTypeMap {
945 pub fn new() -> Self {
947 let mut map = FfiTypeMap::default();
948 map.register("Nat", FfiNativeType::U64, LcnfType::Nat);
949 map.register("String", FfiNativeType::CStr, LcnfType::LcnfString);
950 map.register("Unit", FfiNativeType::Void, LcnfType::Unit);
951 map.register("UInt8", FfiNativeType::U8, LcnfType::Nat);
952 map.register("UInt16", FfiNativeType::U16, LcnfType::Nat);
953 map.register("UInt32", FfiNativeType::U32, LcnfType::Nat);
954 map.register("UInt64", FfiNativeType::U64, LcnfType::Nat);
955 map.register("Int8", FfiNativeType::I8, LcnfType::Nat);
956 map.register("Int16", FfiNativeType::I16, LcnfType::Nat);
957 map.register("Int32", FfiNativeType::I32, LcnfType::Nat);
958 map.register("Int64", FfiNativeType::I64, LcnfType::Nat);
959 map.register("Float", FfiNativeType::F64, LcnfType::Nat);
960 map.register("Bool", FfiNativeType::Bool, LcnfType::Nat);
961 map
962 }
963 pub fn register(&mut self, name: &str, native: FfiNativeType, lcnf: LcnfType) {
965 self.lcnf_to_native.insert(name.to_string(), native.clone());
966 self.native_to_lcnf.insert(native.to_string(), lcnf);
967 }
968 pub fn register_marshal(&mut self, name: &str, info: FfiMarshalInfo) {
970 self.custom_marshal.insert(name.to_string(), info);
971 }
972 pub fn to_native(&self, name: &str) -> Option<&FfiNativeType> {
974 self.lcnf_to_native.get(name)
975 }
976 pub fn to_lcnf(&self, native_name: &str) -> Option<&LcnfType> {
978 self.native_to_lcnf.get(native_name)
979 }
980 pub fn get_marshal(&self, name: &str) -> Option<&FfiMarshalInfo> {
982 self.custom_marshal.get(name)
983 }
984}
985#[allow(dead_code)]
987#[derive(Debug, Clone)]
988pub struct FfiCallbackType {
989 pub name: String,
990 pub ret_type: String,
991 pub params: Vec<FfiFuncParam>,
992 pub calling_conv: FfiCallingConv,
993}
994#[allow(dead_code)]
995#[derive(Debug, Clone)]
996pub struct FFIWorklist {
997 pub(super) items: std::collections::VecDeque<u32>,
998 pub(super) in_worklist: std::collections::HashSet<u32>,
999}
1000impl FFIWorklist {
1001 #[allow(dead_code)]
1002 pub fn new() -> Self {
1003 FFIWorklist {
1004 items: std::collections::VecDeque::new(),
1005 in_worklist: std::collections::HashSet::new(),
1006 }
1007 }
1008 #[allow(dead_code)]
1009 pub fn push(&mut self, item: u32) -> bool {
1010 if self.in_worklist.insert(item) {
1011 self.items.push_back(item);
1012 true
1013 } else {
1014 false
1015 }
1016 }
1017 #[allow(dead_code)]
1018 pub fn pop(&mut self) -> Option<u32> {
1019 let item = self.items.pop_front()?;
1020 self.in_worklist.remove(&item);
1021 Some(item)
1022 }
1023 #[allow(dead_code)]
1024 pub fn is_empty(&self) -> bool {
1025 self.items.is_empty()
1026 }
1027 #[allow(dead_code)]
1028 pub fn len(&self) -> usize {
1029 self.items.len()
1030 }
1031 #[allow(dead_code)]
1032 pub fn contains(&self, item: u32) -> bool {
1033 self.in_worklist.contains(&item)
1034 }
1035}
1036#[allow(dead_code)]
1037#[derive(Debug, Clone)]
1038pub struct FFIDepGraph {
1039 pub(super) nodes: Vec<u32>,
1040 pub(super) edges: Vec<(u32, u32)>,
1041}
1042impl FFIDepGraph {
1043 #[allow(dead_code)]
1044 pub fn new() -> Self {
1045 FFIDepGraph {
1046 nodes: Vec::new(),
1047 edges: Vec::new(),
1048 }
1049 }
1050 #[allow(dead_code)]
1051 pub fn add_node(&mut self, id: u32) {
1052 if !self.nodes.contains(&id) {
1053 self.nodes.push(id);
1054 }
1055 }
1056 #[allow(dead_code)]
1057 pub fn add_dep(&mut self, dep: u32, dependent: u32) {
1058 self.add_node(dep);
1059 self.add_node(dependent);
1060 self.edges.push((dep, dependent));
1061 }
1062 #[allow(dead_code)]
1063 pub fn dependents_of(&self, node: u32) -> Vec<u32> {
1064 self.edges
1065 .iter()
1066 .filter(|(d, _)| *d == node)
1067 .map(|(_, dep)| *dep)
1068 .collect()
1069 }
1070 #[allow(dead_code)]
1071 pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
1072 self.edges
1073 .iter()
1074 .filter(|(_, dep)| *dep == node)
1075 .map(|(d, _)| *d)
1076 .collect()
1077 }
1078 #[allow(dead_code)]
1079 pub fn topological_sort(&self) -> Vec<u32> {
1080 let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
1081 for &n in &self.nodes {
1082 in_degree.insert(n, 0);
1083 }
1084 for (_, dep) in &self.edges {
1085 *in_degree.entry(*dep).or_insert(0) += 1;
1086 }
1087 let mut queue: std::collections::VecDeque<u32> = self
1088 .nodes
1089 .iter()
1090 .filter(|&&n| in_degree[&n] == 0)
1091 .copied()
1092 .collect();
1093 let mut result = Vec::new();
1094 while let Some(node) = queue.pop_front() {
1095 result.push(node);
1096 for dep in self.dependents_of(node) {
1097 let cnt = in_degree.entry(dep).or_insert(0);
1098 *cnt = cnt.saturating_sub(1);
1099 if *cnt == 0 {
1100 queue.push_back(dep);
1101 }
1102 }
1103 }
1104 result
1105 }
1106 #[allow(dead_code)]
1107 pub fn has_cycle(&self) -> bool {
1108 self.topological_sort().len() < self.nodes.len()
1109 }
1110}
1111#[allow(dead_code)]
1113#[derive(Debug, Clone)]
1114pub struct FfiErrorCode {
1115 pub value: i32,
1116 pub name: String,
1117 pub description: String,
1118}
1119#[allow(dead_code)]
1121#[derive(Debug, Default, Clone)]
1122pub struct FfiCodeStats {
1123 pub functions: usize,
1124 pub structs: usize,
1125 pub enums: usize,
1126 pub typedefs: usize,
1127 pub constants: usize,
1128 pub callbacks: usize,
1129 pub total_bytes: usize,
1130}
1131#[allow(dead_code)]
1133#[derive(Debug, Default)]
1134pub struct FfiRustBindingsFile {
1135 pub preamble: String,
1136 pub extern_c: Vec<FfiRustBinding>,
1137 pub types: Vec<FfiRustBinding>,
1138 pub constants: Vec<FfiRustBinding>,
1139}
1140#[allow(dead_code)]
1141impl FfiRustBindingsFile {
1142 pub fn new() -> Self {
1143 Self::default()
1144 }
1145 pub fn add_extern(&mut self, b: FfiRustBinding) {
1146 self.extern_c.push(b);
1147 }
1148 pub fn add_type(&mut self, b: FfiRustBinding) {
1149 self.types.push(b);
1150 }
1151 pub fn add_const(&mut self, b: FfiRustBinding) {
1152 self.constants.push(b);
1153 }
1154 pub fn emit(&self) -> String {
1155 let mut out = self.preamble.clone();
1156 if !self.types.is_empty() {
1157 out.push_str("\n// --- Types ---\n");
1158 for t in &self.types {
1159 out.push_str(&format!("{}\n", t.source));
1160 }
1161 }
1162 if !self.constants.is_empty() {
1163 out.push_str("\n// --- Constants ---\n");
1164 for c in &self.constants {
1165 out.push_str(&format!("{}\n", c.source));
1166 }
1167 }
1168 if !self.extern_c.is_empty() {
1169 out.push_str("\nextern \"C\" {\n");
1170 for e in &self.extern_c {
1171 out.push_str(&format!(" {}\n", e.source));
1172 }
1173 out.push_str("}\n");
1174 }
1175 out
1176 }
1177}
1178#[allow(dead_code)]
1180#[derive(Debug, Clone)]
1181pub struct FfiTypedef {
1182 pub name: String,
1183 pub base_type: String,
1184}
1185#[derive(Debug, Clone)]
1187pub enum FfiError {
1188 UnsupportedType(String),
1190 InvalidCallingConvention(CallingConvention, String),
1192 ParamCountMismatch { expected: usize, found: usize },
1194 TypeMismatch {
1196 param: String,
1197 expected: String,
1198 found: String,
1199 },
1200 StructTooLarge { name: String, size: usize },
1202 RecursiveType(String),
1204 Other(String),
1206}
1207pub struct FfiBridge {
1212 pub(super) type_map: FfiTypeMap,
1214 pub(super) declarations: Vec<FfiDecl>,
1216}
1217impl FfiBridge {
1218 pub fn new() -> Self {
1220 FfiBridge {
1221 type_map: FfiTypeMap::new(),
1222 declarations: Vec::new(),
1223 }
1224 }
1225 pub fn with_type_map(type_map: FfiTypeMap) -> Self {
1227 FfiBridge {
1228 type_map,
1229 declarations: Vec::new(),
1230 }
1231 }
1232 pub fn generate_bindings(&self, decls: &[FfiDecl]) -> FfiOutput {
1234 let mut output = FfiOutput::new();
1235 output.rust_bindings = self.generate_extern_block(decls);
1236 output.c_header = self.generate_c_header(decls);
1237 for decl in decls {
1238 let wrapper = self.generate_c_wrapper(decl);
1239 output.wrapper_fns.push(wrapper);
1240 }
1241 output
1242 }
1243 pub fn generate_extern_block(&self, decls: &[FfiDecl]) -> String {
1245 let mut code = String::new();
1246 code.push_str("// Auto-generated FFI bindings for OxiLean\n\n");
1247 let mut by_cc: BTreeMap<String, Vec<&FfiDecl>> = BTreeMap::new();
1248 for decl in decls {
1249 let cc = match decl.calling_conv {
1250 CallingConvention::C => "C",
1251 CallingConvention::Rust => "Rust",
1252 CallingConvention::System => "system",
1253 CallingConvention::Fastcall => "fastcall",
1254 };
1255 by_cc.entry(cc.to_string()).or_default().push(decl);
1256 }
1257 for (cc, cc_decls) in &by_cc {
1258 code.push_str(&format!("extern \"{}\" {{\n", cc));
1259 for decl in cc_decls {
1260 code.push_str(" ");
1261 if decl.is_unsafe {
1262 code.push_str("// unsafe\n ");
1263 }
1264 code.push_str(&format!("fn {}(", decl.extern_name));
1265 for (i, (pname, pty)) in decl.params.iter().enumerate() {
1266 if i > 0 {
1267 code.push_str(", ");
1268 }
1269 code.push_str(&format!("{}: {}", pname, pty.to_rust_type()));
1270 }
1271 let ret_str = decl.ret_type.to_rust_type();
1272 if ret_str == "()" {
1273 code.push_str(");\n");
1274 } else {
1275 code.push_str(&format!(") -> {};\n", ret_str));
1276 }
1277 }
1278 code.push_str("}\n\n");
1279 }
1280 code
1281 }
1282 pub(super) fn generate_c_header(&self, decls: &[FfiDecl]) -> String {
1284 let mut code = String::new();
1285 code.push_str("/* Auto-generated C header for OxiLean FFI */\n\n");
1286 code.push_str("#ifndef OXILEAN_FFI_H\n");
1287 code.push_str("#define OXILEAN_FFI_H\n\n");
1288 code.push_str("#include <stdint.h>\n");
1289 code.push_str("#include <stddef.h>\n");
1290 code.push_str("#include <stdbool.h>\n");
1291 code.push_str("#include \"lean_runtime.h\"\n\n");
1292 code.push_str("#ifdef __cplusplus\n");
1293 code.push_str("extern \"C\" {\n");
1294 code.push_str("#endif\n\n");
1295 for decl in decls {
1296 code.push_str(&format!("/* {} */\n", decl.name));
1297 code.push_str(&format!("{} {}(", decl.ret_type, decl.extern_name));
1298 for (i, (pname, pty)) in decl.params.iter().enumerate() {
1299 if i > 0 {
1300 code.push_str(", ");
1301 }
1302 code.push_str(&format!("{} {}", pty, pname));
1303 }
1304 if decl.params.is_empty() {
1305 code.push_str("void");
1306 }
1307 code.push_str(");\n\n");
1308 }
1309 code.push_str("#ifdef __cplusplus\n");
1310 code.push_str("}\n");
1311 code.push_str("#endif\n\n");
1312 code.push_str("#endif /* OXILEAN_FFI_H */\n");
1313 code
1314 }
1315 pub fn generate_c_wrapper(&self, decl: &FfiDecl) -> String {
1318 let mut code = String::new();
1319 code.push_str(&format!("/* Wrapper for {} */\n", decl.name));
1320 code.push_str(&format!("{} {}_wrapper(", decl.ret_type, decl.extern_name));
1321 for (i, (pname, pty)) in decl.params.iter().enumerate() {
1322 if i > 0 {
1323 code.push_str(", ");
1324 }
1325 code.push_str(&format!("{} {}", pty, pname));
1326 }
1327 if decl.params.is_empty() {
1328 code.push_str("void");
1329 }
1330 code.push_str(") {\n");
1331 let mut lean_args = Vec::new();
1332 for (pname, pty) in &decl.params {
1333 let marshal = marshal_native_to_lean(pty, pname);
1334 if marshal.is_trivial {
1335 lean_args.push(pname.clone());
1336 } else {
1337 let lean_var = format!("_lean_{}", pname);
1338 code.push_str(&format!(
1339 " lean_object* {} = {};\n",
1340 lean_var,
1341 marshal.to_native.replace("${arg}", pname),
1342 ));
1343 lean_args.push(lean_var);
1344 }
1345 }
1346 let mangled_name = mangle_lean_name(&decl.name);
1347 if decl.ret_type == FfiNativeType::Void {
1348 code.push_str(&format!(" {}(", mangled_name));
1349 code.push_str(&lean_args.join(", "));
1350 code.push_str(");\n");
1351 } else {
1352 code.push_str(&format!(" lean_object* _result = {}(", mangled_name));
1353 code.push_str(&lean_args.join(", "));
1354 code.push_str(");\n");
1355 let ret_marshal = marshal_lean_to_native(&decl.ret_type, "_result");
1356 if ret_marshal.is_trivial {
1357 code.push_str(" return _result;\n");
1358 } else {
1359 code.push_str(&format!(
1360 " {} _native_result = {};\n",
1361 decl.ret_type,
1362 ret_marshal.from_native.replace("${result}", "_result"),
1363 ));
1364 code.push_str(" lean_dec_ref(_result);\n");
1365 code.push_str(" return _native_result;\n");
1366 }
1367 }
1368 code.push_str("}\n");
1369 code
1370 }
1371 pub fn type_map(&self) -> &FfiTypeMap {
1373 &self.type_map
1374 }
1375 pub fn type_map_mut(&mut self) -> &mut FfiTypeMap {
1377 &mut self.type_map
1378 }
1379}
1380#[derive(Debug, Default)]
1382pub struct FfiExtProfiler {
1383 pub(super) timings: Vec<FfiExtPassTiming>,
1384}
1385impl FfiExtProfiler {
1386 pub fn new() -> Self {
1387 FfiExtProfiler::default()
1388 }
1389 pub fn record(&mut self, t: FfiExtPassTiming) {
1390 self.timings.push(t);
1391 }
1392 pub fn total_elapsed_us(&self) -> u64 {
1393 self.timings.iter().map(|t| t.elapsed_us).sum()
1394 }
1395 pub fn slowest_pass(&self) -> Option<&FfiExtPassTiming> {
1396 self.timings.iter().max_by_key(|t| t.elapsed_us)
1397 }
1398 pub fn num_passes(&self) -> usize {
1399 self.timings.len()
1400 }
1401 pub fn profitable_passes(&self) -> Vec<&FfiExtPassTiming> {
1402 self.timings.iter().filter(|t| t.is_profitable()).collect()
1403 }
1404}
1405#[allow(dead_code)]
1407#[derive(Debug, Clone)]
1408pub enum FfiSizeHint {
1409 Fixed(u64),
1410 Param(String),
1411 Dynamic,
1412}
1413#[allow(dead_code)]
1415#[derive(Debug, Clone)]
1416pub struct FfiRustBinding {
1417 pub name: String,
1418 pub kind: FfiRustBindingKind,
1419 pub source: String,
1420}
1421#[allow(dead_code)]
1423#[derive(Debug, Default, Clone)]
1424pub struct FfiPassSummary {
1425 pub pass_name: String,
1426 pub symbols_bridged: usize,
1427 pub headers_emitted: usize,
1428 pub rust_bindings_emitted: usize,
1429 pub duration_us: u64,
1430}
1431#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1433pub enum CallingConvention {
1434 C,
1436 Rust,
1438 System,
1440 Fastcall,
1442}
1443#[allow(dead_code)]
1445#[derive(Debug, Clone)]
1446pub enum FfiReturnWrap {
1447 Direct(String),
1448 ErrorCode(String, String),
1449 OutParam(String),
1450 Bool(String),
1451}
1452#[allow(dead_code)]
1454#[derive(Debug, Clone)]
1455pub struct FfiObjectDesc {
1456 pub c_type: String,
1457 pub rust_type: String,
1458 pub lifetime: FfiLifetime,
1459 pub destroy_fn: Option<String>,
1460 pub clone_fn: Option<String>,
1461}
1462#[allow(dead_code)]
1464#[derive(Debug, Clone)]
1465pub struct FfiExtConfigV2 {
1466 pub platform: FfiPlatformTarget,
1467 pub emit_c_header: bool,
1468 pub emit_rust_bindings: bool,
1469 pub emit_python_cffi: bool,
1470 pub emit_zig_bindings: bool,
1471 pub header_guard_prefix: String,
1472 pub lib_name: String,
1473 pub calling_conv_default: FfiCallingConv,
1474 pub enable_null_checks: bool,
1475 pub enable_bounds_checks: bool,
1476}
1477#[allow(dead_code)]
1479#[derive(Debug, Clone, Default)]
1480pub struct FfiFeatureFlags {
1481 pub support_varargs: bool,
1482 pub support_callbacks: bool,
1483 pub support_bitfields: bool,
1484 pub support_anonymous: bool,
1485 pub support_packed: bool,
1486 pub support_aligned: bool,
1487}
1488#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1490pub struct FfiExtIncrKey {
1491 pub content_hash: u64,
1492 pub config_hash: u64,
1493}
1494impl FfiExtIncrKey {
1495 pub fn new(content: u64, config: u64) -> Self {
1496 FfiExtIncrKey {
1497 content_hash: content,
1498 config_hash: config,
1499 }
1500 }
1501 pub fn combined_hash(&self) -> u64 {
1502 self.content_hash.wrapping_mul(0x9e3779b97f4a7c15) ^ self.config_hash
1503 }
1504 pub fn matches(&self, other: &FfiExtIncrKey) -> bool {
1505 self.content_hash == other.content_hash && self.config_hash == other.config_hash
1506 }
1507}
1508#[derive(Debug, Clone, Default)]
1510pub struct FfiExtConfig {
1511 pub(super) entries: std::collections::HashMap<String, String>,
1512}
1513impl FfiExtConfig {
1514 pub fn new() -> Self {
1515 FfiExtConfig::default()
1516 }
1517 pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
1518 self.entries.insert(key.into(), value.into());
1519 }
1520 pub fn get(&self, key: &str) -> Option<&str> {
1521 self.entries.get(key).map(|s| s.as_str())
1522 }
1523 pub fn get_bool(&self, key: &str) -> bool {
1524 matches!(self.get(key), Some("true") | Some("1") | Some("yes"))
1525 }
1526 pub fn get_int(&self, key: &str) -> Option<i64> {
1527 self.get(key)?.parse().ok()
1528 }
1529 pub fn len(&self) -> usize {
1530 self.entries.len()
1531 }
1532 pub fn is_empty(&self) -> bool {
1533 self.entries.is_empty()
1534 }
1535}
1536#[allow(dead_code)]
1538#[derive(Debug, Default)]
1539pub struct FfiBridgeSummaryV2 {
1540 pub lib_name: String,
1541 pub platform: String,
1542 pub funcs: usize,
1543 pub structs: usize,
1544 pub enums: usize,
1545 pub bytes: usize,
1546}
1547#[allow(dead_code)]
1548pub struct FFIConstantFoldingHelper;
1549impl FFIConstantFoldingHelper {
1550 #[allow(dead_code)]
1551 pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1552 a.checked_add(b)
1553 }
1554 #[allow(dead_code)]
1555 pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1556 a.checked_sub(b)
1557 }
1558 #[allow(dead_code)]
1559 pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1560 a.checked_mul(b)
1561 }
1562 #[allow(dead_code)]
1563 pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1564 if b == 0 {
1565 None
1566 } else {
1567 a.checked_div(b)
1568 }
1569 }
1570 #[allow(dead_code)]
1571 pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1572 a + b
1573 }
1574 #[allow(dead_code)]
1575 pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1576 a * b
1577 }
1578 #[allow(dead_code)]
1579 pub fn fold_neg_i64(a: i64) -> Option<i64> {
1580 a.checked_neg()
1581 }
1582 #[allow(dead_code)]
1583 pub fn fold_not_bool(a: bool) -> bool {
1584 !a
1585 }
1586 #[allow(dead_code)]
1587 pub fn fold_and_bool(a: bool, b: bool) -> bool {
1588 a && b
1589 }
1590 #[allow(dead_code)]
1591 pub fn fold_or_bool(a: bool, b: bool) -> bool {
1592 a || b
1593 }
1594 #[allow(dead_code)]
1595 pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
1596 a.checked_shl(b)
1597 }
1598 #[allow(dead_code)]
1599 pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
1600 a.checked_shr(b)
1601 }
1602 #[allow(dead_code)]
1603 pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
1604 if b == 0 {
1605 None
1606 } else {
1607 Some(a % b)
1608 }
1609 }
1610 #[allow(dead_code)]
1611 pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
1612 a & b
1613 }
1614 #[allow(dead_code)]
1615 pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
1616 a | b
1617 }
1618 #[allow(dead_code)]
1619 pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
1620 a ^ b
1621 }
1622 #[allow(dead_code)]
1623 pub fn fold_bitnot_i64(a: i64) -> i64 {
1624 !a
1625 }
1626}
1627#[allow(dead_code)]
1628#[derive(Debug, Clone)]
1629pub struct FFIAnalysisCache {
1630 pub(super) entries: std::collections::HashMap<String, FFICacheEntry>,
1631 pub(super) max_size: usize,
1632 pub(super) hits: u64,
1633 pub(super) misses: u64,
1634}
1635impl FFIAnalysisCache {
1636 #[allow(dead_code)]
1637 pub fn new(max_size: usize) -> Self {
1638 FFIAnalysisCache {
1639 entries: std::collections::HashMap::new(),
1640 max_size,
1641 hits: 0,
1642 misses: 0,
1643 }
1644 }
1645 #[allow(dead_code)]
1646 pub fn get(&mut self, key: &str) -> Option<&FFICacheEntry> {
1647 if self.entries.contains_key(key) {
1648 self.hits += 1;
1649 self.entries.get(key)
1650 } else {
1651 self.misses += 1;
1652 None
1653 }
1654 }
1655 #[allow(dead_code)]
1656 pub fn insert(&mut self, key: String, data: Vec<u8>) {
1657 if self.entries.len() >= self.max_size {
1658 if let Some(oldest) = self.entries.keys().next().cloned() {
1659 self.entries.remove(&oldest);
1660 }
1661 }
1662 self.entries.insert(
1663 key.clone(),
1664 FFICacheEntry {
1665 key,
1666 data,
1667 timestamp: 0,
1668 valid: true,
1669 },
1670 );
1671 }
1672 #[allow(dead_code)]
1673 pub fn invalidate(&mut self, key: &str) {
1674 if let Some(entry) = self.entries.get_mut(key) {
1675 entry.valid = false;
1676 }
1677 }
1678 #[allow(dead_code)]
1679 pub fn clear(&mut self) {
1680 self.entries.clear();
1681 }
1682 #[allow(dead_code)]
1683 pub fn hit_rate(&self) -> f64 {
1684 let total = self.hits + self.misses;
1685 if total == 0 {
1686 return 0.0;
1687 }
1688 self.hits as f64 / total as f64
1689 }
1690 #[allow(dead_code)]
1691 pub fn size(&self) -> usize {
1692 self.entries.len()
1693 }
1694}
1695#[allow(dead_code)]
1697#[derive(Debug, Clone)]
1698pub struct FfiStructField {
1699 pub name: String,
1700 pub field_type: String,
1701 pub bit_width: Option<u8>,
1702 pub offset_bytes: Option<u64>,
1703 pub attrs: Vec<String>,
1704}
1705#[allow(dead_code)]
1706#[derive(Debug, Clone)]
1707pub struct FFILivenessInfo {
1708 pub live_in: Vec<std::collections::HashSet<u32>>,
1709 pub live_out: Vec<std::collections::HashSet<u32>>,
1710 pub defs: Vec<std::collections::HashSet<u32>>,
1711 pub uses: Vec<std::collections::HashSet<u32>>,
1712}
1713impl FFILivenessInfo {
1714 #[allow(dead_code)]
1715 pub fn new(block_count: usize) -> Self {
1716 FFILivenessInfo {
1717 live_in: vec![std::collections::HashSet::new(); block_count],
1718 live_out: vec![std::collections::HashSet::new(); block_count],
1719 defs: vec![std::collections::HashSet::new(); block_count],
1720 uses: vec![std::collections::HashSet::new(); block_count],
1721 }
1722 }
1723 #[allow(dead_code)]
1724 pub fn add_def(&mut self, block: usize, var: u32) {
1725 if block < self.defs.len() {
1726 self.defs[block].insert(var);
1727 }
1728 }
1729 #[allow(dead_code)]
1730 pub fn add_use(&mut self, block: usize, var: u32) {
1731 if block < self.uses.len() {
1732 self.uses[block].insert(var);
1733 }
1734 }
1735 #[allow(dead_code)]
1736 pub fn is_live_in(&self, block: usize, var: u32) -> bool {
1737 self.live_in
1738 .get(block)
1739 .map(|s| s.contains(&var))
1740 .unwrap_or(false)
1741 }
1742 #[allow(dead_code)]
1743 pub fn is_live_out(&self, block: usize, var: u32) -> bool {
1744 self.live_out
1745 .get(block)
1746 .map(|s| s.contains(&var))
1747 .unwrap_or(false)
1748 }
1749}
1750#[allow(dead_code)]
1752#[derive(Debug, Clone, PartialEq, Eq)]
1753pub enum FfiLifetime {
1754 Static,
1755 ScopedToCall,
1756 CallerManaged,
1757 LibraryManaged,
1758 RefCounted,
1759}
1760#[allow(dead_code)]
1762#[derive(Debug, Clone)]
1763pub struct FfiStructDef {
1764 pub name: String,
1765 pub fields: Vec<FfiStructField>,
1766 pub alignment: Option<u64>,
1767 pub is_packed: bool,
1768 pub is_union: bool,
1769}
1770#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1772pub enum FfiExtDiagSeverity {
1773 Note,
1774 Warning,
1775 Error,
1776}
1777#[allow(dead_code)]
1779#[derive(Debug, Clone)]
1780pub struct FfiFuncSignature {
1781 pub name: String,
1782 pub params: Vec<FfiFuncParam>,
1783 pub ret_type: String,
1784 pub calling_conv: FfiCallingConv,
1785 pub is_async: bool,
1786 pub is_noexcept: bool,
1787 pub is_nothrow: bool,
1788}
1789#[derive(Debug, Default)]
1791pub struct FfiExtSourceBuffer {
1792 pub(super) buf: String,
1793 pub(super) indent_level: usize,
1794 pub(super) indent_str: String,
1795}
1796impl FfiExtSourceBuffer {
1797 pub fn new() -> Self {
1798 FfiExtSourceBuffer {
1799 buf: String::new(),
1800 indent_level: 0,
1801 indent_str: " ".to_string(),
1802 }
1803 }
1804 pub fn with_indent(mut self, indent: impl Into<String>) -> Self {
1805 self.indent_str = indent.into();
1806 self
1807 }
1808 pub fn push_line(&mut self, line: &str) {
1809 for _ in 0..self.indent_level {
1810 self.buf.push_str(&self.indent_str);
1811 }
1812 self.buf.push_str(line);
1813 self.buf.push('\n');
1814 }
1815 pub fn push_raw(&mut self, s: &str) {
1816 self.buf.push_str(s);
1817 }
1818 pub fn indent(&mut self) {
1819 self.indent_level += 1;
1820 }
1821 pub fn dedent(&mut self) {
1822 self.indent_level = self.indent_level.saturating_sub(1);
1823 }
1824 pub fn as_str(&self) -> &str {
1825 &self.buf
1826 }
1827 pub fn len(&self) -> usize {
1828 self.buf.len()
1829 }
1830 pub fn is_empty(&self) -> bool {
1831 self.buf.is_empty()
1832 }
1833 pub fn line_count(&self) -> usize {
1834 self.buf.lines().count()
1835 }
1836 pub fn into_string(self) -> String {
1837 self.buf
1838 }
1839 pub fn reset(&mut self) {
1840 self.buf.clear();
1841 self.indent_level = 0;
1842 }
1843}
1844#[allow(dead_code)]
1846#[derive(Debug, Clone)]
1847pub struct FfiFuncParam {
1848 pub name: String,
1849 pub ffi_type: String,
1850 pub attrs: Vec<FfiParamAttr>,
1851 pub is_vararg: bool,
1852}
1853#[derive(Debug, Clone, Default)]
1855pub struct FfiExtEmitStats {
1856 pub bytes_emitted: usize,
1857 pub items_emitted: usize,
1858 pub errors: usize,
1859 pub warnings: usize,
1860 pub elapsed_ms: u64,
1861}
1862impl FfiExtEmitStats {
1863 pub fn new() -> Self {
1864 FfiExtEmitStats::default()
1865 }
1866 pub fn throughput_bps(&self) -> f64 {
1867 if self.elapsed_ms == 0 {
1868 0.0
1869 } else {
1870 self.bytes_emitted as f64 / (self.elapsed_ms as f64 / 1000.0)
1871 }
1872 }
1873 pub fn is_clean(&self) -> bool {
1874 self.errors == 0
1875 }
1876}
1877#[allow(dead_code)]
1879#[derive(Debug, Clone)]
1880pub struct FfiConst {
1881 pub name: String,
1882 pub const_type: String,
1883 pub value: String,
1884}