Skip to main content

oxilean_codegen/riscv_backend/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use std::collections::{HashMap, HashSet, VecDeque};
6
7/// Pass registry for RISCVExt.
8#[allow(dead_code)]
9#[derive(Debug, Default)]
10pub struct RISCVExtPassRegistry {
11    pub(super) configs: Vec<RISCVExtPassConfig>,
12    pub(super) stats: Vec<RISCVExtPassStats>,
13}
14impl RISCVExtPassRegistry {
15    #[allow(dead_code)]
16    pub fn new() -> Self {
17        Self::default()
18    }
19    #[allow(dead_code)]
20    pub fn register(&mut self, c: RISCVExtPassConfig) {
21        self.stats.push(RISCVExtPassStats::new());
22        self.configs.push(c);
23    }
24    #[allow(dead_code)]
25    pub fn len(&self) -> usize {
26        self.configs.len()
27    }
28    #[allow(dead_code)]
29    pub fn is_empty(&self) -> bool {
30        self.configs.is_empty()
31    }
32    #[allow(dead_code)]
33    pub fn get(&self, i: usize) -> Option<&RISCVExtPassConfig> {
34        self.configs.get(i)
35    }
36    #[allow(dead_code)]
37    pub fn get_stats(&self, i: usize) -> Option<&RISCVExtPassStats> {
38        self.stats.get(i)
39    }
40    #[allow(dead_code)]
41    pub fn enabled_passes(&self) -> Vec<&RISCVExtPassConfig> {
42        self.configs.iter().filter(|c| c.enabled).collect()
43    }
44    #[allow(dead_code)]
45    pub fn passes_in_phase(&self, ph: &RISCVExtPassPhase) -> Vec<&RISCVExtPassConfig> {
46        self.configs
47            .iter()
48            .filter(|c| c.enabled && &c.phase == ph)
49            .collect()
50    }
51    #[allow(dead_code)]
52    pub fn total_nodes_visited(&self) -> usize {
53        self.stats.iter().map(|s| s.nodes_visited).sum()
54    }
55    #[allow(dead_code)]
56    pub fn any_changed(&self) -> bool {
57        self.stats.iter().any(|s| s.changed)
58    }
59}
60/// Dependency graph for RISCVX2.
61#[allow(dead_code)]
62#[derive(Debug, Clone)]
63pub struct RISCVX2DepGraph {
64    pub(super) n: usize,
65    pub(super) adj: Vec<Vec<usize>>,
66    pub(super) rev: Vec<Vec<usize>>,
67    pub(super) edge_count: usize,
68}
69impl RISCVX2DepGraph {
70    #[allow(dead_code)]
71    pub fn new(n: usize) -> Self {
72        Self {
73            n,
74            adj: vec![Vec::new(); n],
75            rev: vec![Vec::new(); n],
76            edge_count: 0,
77        }
78    }
79    #[allow(dead_code)]
80    pub fn add_edge(&mut self, from: usize, to: usize) {
81        if from < self.n && to < self.n {
82            if !self.adj[from].contains(&to) {
83                self.adj[from].push(to);
84                self.rev[to].push(from);
85                self.edge_count += 1;
86            }
87        }
88    }
89    #[allow(dead_code)]
90    pub fn succs(&self, n: usize) -> &[usize] {
91        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
92    }
93    #[allow(dead_code)]
94    pub fn preds(&self, n: usize) -> &[usize] {
95        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
96    }
97    #[allow(dead_code)]
98    pub fn topo_sort(&self) -> Option<Vec<usize>> {
99        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
100        let mut q: std::collections::VecDeque<usize> =
101            (0..self.n).filter(|&i| deg[i] == 0).collect();
102        let mut out = Vec::with_capacity(self.n);
103        while let Some(u) = q.pop_front() {
104            out.push(u);
105            for &v in &self.adj[u] {
106                deg[v] -= 1;
107                if deg[v] == 0 {
108                    q.push_back(v);
109                }
110            }
111        }
112        if out.len() == self.n {
113            Some(out)
114        } else {
115            None
116        }
117    }
118    #[allow(dead_code)]
119    pub fn has_cycle(&self) -> bool {
120        self.topo_sort().is_none()
121    }
122    #[allow(dead_code)]
123    pub fn reachable(&self, start: usize) -> Vec<usize> {
124        let mut vis = vec![false; self.n];
125        let mut stk = vec![start];
126        let mut out = Vec::new();
127        while let Some(u) = stk.pop() {
128            if u < self.n && !vis[u] {
129                vis[u] = true;
130                out.push(u);
131                for &v in &self.adj[u] {
132                    if !vis[v] {
133                        stk.push(v);
134                    }
135                }
136            }
137        }
138        out
139    }
140    #[allow(dead_code)]
141    pub fn scc(&self) -> Vec<Vec<usize>> {
142        let mut visited = vec![false; self.n];
143        let mut order = Vec::new();
144        for i in 0..self.n {
145            if !visited[i] {
146                let mut stk = vec![(i, 0usize)];
147                while let Some((u, idx)) = stk.last_mut() {
148                    if !visited[*u] {
149                        visited[*u] = true;
150                    }
151                    if *idx < self.adj[*u].len() {
152                        let v = self.adj[*u][*idx];
153                        *idx += 1;
154                        if !visited[v] {
155                            stk.push((v, 0));
156                        }
157                    } else {
158                        order.push(*u);
159                        stk.pop();
160                    }
161                }
162            }
163        }
164        let mut comp = vec![usize::MAX; self.n];
165        let mut components: Vec<Vec<usize>> = Vec::new();
166        for &start in order.iter().rev() {
167            if comp[start] == usize::MAX {
168                let cid = components.len();
169                let mut component = Vec::new();
170                let mut stk = vec![start];
171                while let Some(u) = stk.pop() {
172                    if comp[u] == usize::MAX {
173                        comp[u] = cid;
174                        component.push(u);
175                        for &v in &self.rev[u] {
176                            if comp[v] == usize::MAX {
177                                stk.push(v);
178                            }
179                        }
180                    }
181                }
182                components.push(component);
183            }
184        }
185        components
186    }
187    #[allow(dead_code)]
188    pub fn node_count(&self) -> usize {
189        self.n
190    }
191    #[allow(dead_code)]
192    pub fn edge_count(&self) -> usize {
193        self.edge_count
194    }
195}
196/// Worklist for RISCVX2.
197#[allow(dead_code)]
198#[derive(Debug, Clone)]
199pub struct RISCVX2Worklist {
200    pub(super) items: std::collections::VecDeque<usize>,
201    pub(super) present: Vec<bool>,
202}
203impl RISCVX2Worklist {
204    #[allow(dead_code)]
205    pub fn new(capacity: usize) -> Self {
206        Self {
207            items: std::collections::VecDeque::new(),
208            present: vec![false; capacity],
209        }
210    }
211    #[allow(dead_code)]
212    pub fn push(&mut self, id: usize) {
213        if id < self.present.len() && !self.present[id] {
214            self.present[id] = true;
215            self.items.push_back(id);
216        }
217    }
218    #[allow(dead_code)]
219    pub fn push_front(&mut self, id: usize) {
220        if id < self.present.len() && !self.present[id] {
221            self.present[id] = true;
222            self.items.push_front(id);
223        }
224    }
225    #[allow(dead_code)]
226    pub fn pop(&mut self) -> Option<usize> {
227        let id = self.items.pop_front()?;
228        if id < self.present.len() {
229            self.present[id] = false;
230        }
231        Some(id)
232    }
233    #[allow(dead_code)]
234    pub fn is_empty(&self) -> bool {
235        self.items.is_empty()
236    }
237    #[allow(dead_code)]
238    pub fn len(&self) -> usize {
239        self.items.len()
240    }
241    #[allow(dead_code)]
242    pub fn contains(&self, id: usize) -> bool {
243        id < self.present.len() && self.present[id]
244    }
245    #[allow(dead_code)]
246    pub fn drain_all(&mut self) -> Vec<usize> {
247        let v: Vec<usize> = self.items.drain(..).collect();
248        for &id in &v {
249            if id < self.present.len() {
250                self.present[id] = false;
251            }
252        }
253        v
254    }
255}
256#[allow(dead_code)]
257#[derive(Debug, Clone)]
258pub struct RvDominatorTree {
259    pub idom: Vec<Option<u32>>,
260    pub dom_children: Vec<Vec<u32>>,
261    pub dom_depth: Vec<u32>,
262}
263impl RvDominatorTree {
264    #[allow(dead_code)]
265    pub fn new(size: usize) -> Self {
266        RvDominatorTree {
267            idom: vec![None; size],
268            dom_children: vec![Vec::new(); size],
269            dom_depth: vec![0; size],
270        }
271    }
272    #[allow(dead_code)]
273    pub fn set_idom(&mut self, node: usize, idom: u32) {
274        self.idom[node] = Some(idom);
275    }
276    #[allow(dead_code)]
277    pub fn dominates(&self, a: usize, b: usize) -> bool {
278        if a == b {
279            return true;
280        }
281        let mut cur = b;
282        loop {
283            match self.idom[cur] {
284                Some(parent) if parent as usize == a => return true,
285                Some(parent) if parent as usize == cur => return false,
286                Some(parent) => cur = parent as usize,
287                None => return false,
288            }
289        }
290    }
291    #[allow(dead_code)]
292    pub fn depth(&self, node: usize) -> u32 {
293        self.dom_depth.get(node).copied().unwrap_or(0)
294    }
295}
296/// RISC-V code-generation backend.
297///
298/// Supports both RV32I (`is_64bit = false`) and RV64I (`is_64bit = true`).
299#[derive(Debug, Clone)]
300pub struct RiscVBackend {
301    pub is_64bit: bool,
302}
303impl RiscVBackend {
304    pub fn new(is_64bit: bool) -> Self {
305        Self { is_64bit }
306    }
307    /// Emit a single instruction as a text line (GNU assembler syntax).
308    pub fn emit_instr(&self, instr: &RiscVInstr) -> String {
309        match instr {
310            RiscVInstr::LUI(rd, imm) => format!("    lui     {}, {}", rd.name(), imm),
311            RiscVInstr::AUIPC(rd, imm) => format!("    auipc   {}, {}", rd.name(), imm),
312            RiscVInstr::JAL(rd, off) => format!("    jal     {}, {}", rd.name(), off),
313            RiscVInstr::JALR(rd, rs1, off) => {
314                format!("    jalr    {}, {}({})", rd.name(), off, rs1.name())
315            }
316            RiscVInstr::BEQ(rs1, rs2, off) => {
317                format!("    beq     {}, {}, {}", rs1.name(), rs2.name(), off)
318            }
319            RiscVInstr::BNE(rs1, rs2, off) => {
320                format!("    bne     {}, {}, {}", rs1.name(), rs2.name(), off)
321            }
322            RiscVInstr::BLT(rs1, rs2, off) => {
323                format!("    blt     {}, {}, {}", rs1.name(), rs2.name(), off)
324            }
325            RiscVInstr::BGE(rs1, rs2, off) => {
326                format!("    bge     {}, {}, {}", rs1.name(), rs2.name(), off)
327            }
328            RiscVInstr::BLTU(rs1, rs2, off) => {
329                format!("    bltu    {}, {}, {}", rs1.name(), rs2.name(), off)
330            }
331            RiscVInstr::BGEU(rs1, rs2, off) => {
332                format!("    bgeu    {}, {}, {}", rs1.name(), rs2.name(), off)
333            }
334            RiscVInstr::LB(rd, rs1, off) => {
335                format!("    lb      {}, {}({})", rd.name(), off, rs1.name())
336            }
337            RiscVInstr::LH(rd, rs1, off) => {
338                format!("    lh      {}, {}({})", rd.name(), off, rs1.name())
339            }
340            RiscVInstr::LW(rd, rs1, off) => {
341                format!("    lw      {}, {}({})", rd.name(), off, rs1.name())
342            }
343            RiscVInstr::LBU(rd, rs1, off) => {
344                format!("    lbu     {}, {}({})", rd.name(), off, rs1.name())
345            }
346            RiscVInstr::LHU(rd, rs1, off) => {
347                format!("    lhu     {}, {}({})", rd.name(), off, rs1.name())
348            }
349            RiscVInstr::LD(rd, rs1, off) => {
350                format!("    ld      {}, {}({})", rd.name(), off, rs1.name())
351            }
352            RiscVInstr::LWU(rd, rs1, off) => {
353                format!("    lwu     {}, {}({})", rd.name(), off, rs1.name())
354            }
355            RiscVInstr::SB(rs2, rs1, off) => {
356                format!("    sb      {}, {}({})", rs2.name(), off, rs1.name())
357            }
358            RiscVInstr::SH(rs2, rs1, off) => {
359                format!("    sh      {}, {}({})", rs2.name(), off, rs1.name())
360            }
361            RiscVInstr::SW(rs2, rs1, off) => {
362                format!("    sw      {}, {}({})", rs2.name(), off, rs1.name())
363            }
364            RiscVInstr::SD(rs2, rs1, off) => {
365                format!("    sd      {}, {}({})", rs2.name(), off, rs1.name())
366            }
367            RiscVInstr::ADDI(rd, rs1, imm) => {
368                format!("    addi    {}, {}, {}", rd.name(), rs1.name(), imm)
369            }
370            RiscVInstr::SLTI(rd, rs1, imm) => {
371                format!("    slti    {}, {}, {}", rd.name(), rs1.name(), imm)
372            }
373            RiscVInstr::SLTIU(rd, rs1, imm) => {
374                format!("    sltiu   {}, {}, {}", rd.name(), rs1.name(), imm)
375            }
376            RiscVInstr::XORI(rd, rs1, imm) => {
377                format!("    xori    {}, {}, {}", rd.name(), rs1.name(), imm)
378            }
379            RiscVInstr::ORI(rd, rs1, imm) => {
380                format!("    ori     {}, {}, {}", rd.name(), rs1.name(), imm)
381            }
382            RiscVInstr::ANDI(rd, rs1, imm) => {
383                format!("    andi    {}, {}, {}", rd.name(), rs1.name(), imm)
384            }
385            RiscVInstr::SLLI(rd, rs1, shamt) => {
386                format!("    slli    {}, {}, {}", rd.name(), rs1.name(), shamt)
387            }
388            RiscVInstr::SRLI(rd, rs1, shamt) => {
389                format!("    srli    {}, {}, {}", rd.name(), rs1.name(), shamt)
390            }
391            RiscVInstr::SRAI(rd, rs1, shamt) => {
392                format!("    srai    {}, {}, {}", rd.name(), rs1.name(), shamt)
393            }
394            RiscVInstr::ADDIW(rd, rs1, imm) => {
395                format!("    addiw   {}, {}, {}", rd.name(), rs1.name(), imm)
396            }
397            RiscVInstr::ADD(rd, rs1, rs2) => {
398                format!("    add     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
399            }
400            RiscVInstr::SUB(rd, rs1, rs2) => {
401                format!("    sub     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
402            }
403            RiscVInstr::SLL(rd, rs1, rs2) => {
404                format!("    sll     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
405            }
406            RiscVInstr::SLT(rd, rs1, rs2) => {
407                format!("    slt     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
408            }
409            RiscVInstr::SLTU(rd, rs1, rs2) => {
410                format!("    sltu    {}, {}, {}", rd.name(), rs1.name(), rs2.name())
411            }
412            RiscVInstr::XOR(rd, rs1, rs2) => {
413                format!("    xor     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
414            }
415            RiscVInstr::SRL(rd, rs1, rs2) => {
416                format!("    srl     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
417            }
418            RiscVInstr::SRA(rd, rs1, rs2) => {
419                format!("    sra     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
420            }
421            RiscVInstr::OR(rd, rs1, rs2) => {
422                format!("    or      {}, {}, {}", rd.name(), rs1.name(), rs2.name())
423            }
424            RiscVInstr::AND(rd, rs1, rs2) => {
425                format!("    and     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
426            }
427            RiscVInstr::MUL(rd, rs1, rs2) => {
428                format!("    mul     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
429            }
430            RiscVInstr::MULH(rd, rs1, rs2) => {
431                format!("    mulh    {}, {}, {}", rd.name(), rs1.name(), rs2.name())
432            }
433            RiscVInstr::DIV(rd, rs1, rs2) => {
434                format!("    div     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
435            }
436            RiscVInstr::REM(rd, rs1, rs2) => {
437                format!("    rem     {}, {}, {}", rd.name(), rs1.name(), rs2.name())
438            }
439            RiscVInstr::ECALL => "    ecall".to_string(),
440            RiscVInstr::EBREAK => "    ebreak".to_string(),
441            RiscVInstr::LI(rd, imm) => format!("    li      {}, {}", rd.name(), imm),
442            RiscVInstr::MV(rd, rs) => format!("    mv      {}, {}", rd.name(), rs.name()),
443            RiscVInstr::NOP => "    nop".to_string(),
444            RiscVInstr::RET => "    ret".to_string(),
445            RiscVInstr::CALL(sym) => format!("    call    {}", sym),
446            RiscVInstr::Label(lbl) => format!("{}:", lbl),
447            RiscVInstr::Directive(d, arg) => {
448                if arg.is_empty() {
449                    format!("    .{}", d)
450                } else {
451                    format!("    .{} {}", d, arg)
452                }
453            }
454        }
455    }
456    /// Emit a complete function (with .globl / .type directives and label).
457    pub fn emit_function(&self, func: &RiscVFunction) -> String {
458        let mut out = String::new();
459        out.push_str(&format!("    .globl {}\n", func.name));
460        out.push_str(&format!("    .type  {}, @function\n", func.name));
461        out.push_str(&format!("{}:\n", func.name));
462        for instr in &func.instrs {
463            out.push_str(&self.emit_instr(instr));
464            out.push('\n');
465        }
466        out.push_str(&format!("    .size  {}, .-{}\n", func.name, func.name));
467        out
468    }
469    /// Standard RISC-V function prologue.
470    ///
471    /// Saves `ra` and `s0` (frame pointer), sets up a stack frame of
472    /// `frame_size` bytes (rounded up to 16-byte alignment).
473    pub fn prologue(&self, frame_size: u32) -> Vec<RiscVInstr> {
474        let aligned = frame_size.div_ceil(16) * 16;
475        let neg = -(aligned as i16);
476        let (store_ra, store_fp, set_fp) = if self.is_64bit {
477            (
478                RiscVInstr::SD(RiscVReg::Ra, RiscVReg::Sp, (aligned as i16) - 8),
479                RiscVInstr::SD(RiscVReg::S0, RiscVReg::Sp, (aligned as i16) - 16),
480                RiscVInstr::ADDI(RiscVReg::S0, RiscVReg::Sp, aligned as i16),
481            )
482        } else {
483            (
484                RiscVInstr::SW(RiscVReg::Ra, RiscVReg::Sp, (aligned as i16) - 4),
485                RiscVInstr::SW(RiscVReg::S0, RiscVReg::Sp, (aligned as i16) - 8),
486                RiscVInstr::ADDI(RiscVReg::S0, RiscVReg::Sp, aligned as i16),
487            )
488        };
489        vec![
490            RiscVInstr::ADDI(RiscVReg::Sp, RiscVReg::Sp, neg),
491            store_ra,
492            store_fp,
493            set_fp,
494        ]
495    }
496    /// Standard RISC-V function epilogue (restores ra/s0, deallocates frame).
497    pub fn epilogue(&self) -> Vec<RiscVInstr> {
498        let (load_ra, load_fp) = if self.is_64bit {
499            (
500                RiscVInstr::LD(RiscVReg::Ra, RiscVReg::S0, -8),
501                RiscVInstr::LD(RiscVReg::S0, RiscVReg::S0, -16),
502            )
503        } else {
504            (
505                RiscVInstr::LW(RiscVReg::Ra, RiscVReg::S0, -4),
506                RiscVInstr::LW(RiscVReg::S0, RiscVReg::S0, -8),
507            )
508        };
509        vec![
510            load_ra,
511            load_fp,
512            RiscVInstr::MV(RiscVReg::Sp, RiscVReg::S0),
513            RiscVInstr::RET,
514        ]
515    }
516    /// Argument registers per the RISC-V calling convention (a0 .. a7).
517    pub fn calling_convention_args() -> Vec<RiscVReg> {
518        vec![
519            RiscVReg::A0,
520            RiscVReg::A1,
521            RiscVReg::A2,
522            RiscVReg::A3,
523            RiscVReg::A4,
524            RiscVReg::A5,
525            RiscVReg::A6,
526            RiscVReg::A7,
527        ]
528    }
529    /// Caller-saved (temporary) registers: t0 .. t6.
530    pub fn caller_saved() -> Vec<RiscVReg> {
531        vec![
532            RiscVReg::T0,
533            RiscVReg::T1,
534            RiscVReg::T2,
535            RiscVReg::T3,
536            RiscVReg::T4,
537            RiscVReg::T5,
538            RiscVReg::T6,
539        ]
540    }
541    /// Callee-saved registers: s0 .. s11.
542    pub fn callee_saved() -> Vec<RiscVReg> {
543        vec![
544            RiscVReg::S0,
545            RiscVReg::S1,
546            RiscVReg::S2,
547            RiscVReg::S3,
548            RiscVReg::S4,
549            RiscVReg::S5,
550            RiscVReg::S6,
551            RiscVReg::S7,
552            RiscVReg::S8,
553            RiscVReg::S9,
554            RiscVReg::S10,
555            RiscVReg::S11,
556        ]
557    }
558}
559/// Liveness analysis for RISCVExt.
560#[allow(dead_code)]
561#[derive(Debug, Clone, Default)]
562pub struct RISCVExtLiveness {
563    pub live_in: Vec<Vec<usize>>,
564    pub live_out: Vec<Vec<usize>>,
565    pub defs: Vec<Vec<usize>>,
566    pub uses: Vec<Vec<usize>>,
567}
568impl RISCVExtLiveness {
569    #[allow(dead_code)]
570    pub fn new(n: usize) -> Self {
571        Self {
572            live_in: vec![Vec::new(); n],
573            live_out: vec![Vec::new(); n],
574            defs: vec![Vec::new(); n],
575            uses: vec![Vec::new(); n],
576        }
577    }
578    #[allow(dead_code)]
579    pub fn live_in(&self, b: usize, v: usize) -> bool {
580        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
581    }
582    #[allow(dead_code)]
583    pub fn live_out(&self, b: usize, v: usize) -> bool {
584        self.live_out
585            .get(b)
586            .map(|s| s.contains(&v))
587            .unwrap_or(false)
588    }
589    #[allow(dead_code)]
590    pub fn add_def(&mut self, b: usize, v: usize) {
591        if let Some(s) = self.defs.get_mut(b) {
592            if !s.contains(&v) {
593                s.push(v);
594            }
595        }
596    }
597    #[allow(dead_code)]
598    pub fn add_use(&mut self, b: usize, v: usize) {
599        if let Some(s) = self.uses.get_mut(b) {
600            if !s.contains(&v) {
601                s.push(v);
602            }
603        }
604    }
605    #[allow(dead_code)]
606    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
607        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
608    }
609    #[allow(dead_code)]
610    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
611        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
612    }
613}
614/// Analysis cache for RISCVExt.
615#[allow(dead_code)]
616#[derive(Debug)]
617pub struct RISCVExtCache {
618    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
619    pub(super) cap: usize,
620    pub(super) total_hits: u64,
621    pub(super) total_misses: u64,
622}
623impl RISCVExtCache {
624    #[allow(dead_code)]
625    pub fn new(cap: usize) -> Self {
626        Self {
627            entries: Vec::new(),
628            cap,
629            total_hits: 0,
630            total_misses: 0,
631        }
632    }
633    #[allow(dead_code)]
634    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
635        for e in self.entries.iter_mut() {
636            if e.0 == key && e.2 {
637                e.3 += 1;
638                self.total_hits += 1;
639                return Some(&e.1);
640            }
641        }
642        self.total_misses += 1;
643        None
644    }
645    #[allow(dead_code)]
646    pub fn put(&mut self, key: u64, data: Vec<u8>) {
647        if self.entries.len() >= self.cap {
648            self.entries.retain(|e| e.2);
649            if self.entries.len() >= self.cap {
650                self.entries.remove(0);
651            }
652        }
653        self.entries.push((key, data, true, 0));
654    }
655    #[allow(dead_code)]
656    pub fn invalidate(&mut self) {
657        for e in self.entries.iter_mut() {
658            e.2 = false;
659        }
660    }
661    #[allow(dead_code)]
662    pub fn hit_rate(&self) -> f64 {
663        let t = self.total_hits + self.total_misses;
664        if t == 0 {
665            0.0
666        } else {
667            self.total_hits as f64 / t as f64
668        }
669    }
670    #[allow(dead_code)]
671    pub fn live_count(&self) -> usize {
672        self.entries.iter().filter(|e| e.2).count()
673    }
674}
675/// RISC-V register set (integer registers, ABI names).
676#[derive(Debug, Clone, PartialEq)]
677pub enum RiscVReg {
678    Zero,
679    Ra,
680    Sp,
681    Gp,
682    Tp,
683    T0,
684    T1,
685    T2,
686    S0,
687    S1,
688    A0,
689    A1,
690    A2,
691    A3,
692    A4,
693    A5,
694    A6,
695    A7,
696    S2,
697    S3,
698    S4,
699    S5,
700    S6,
701    S7,
702    S8,
703    S9,
704    S10,
705    S11,
706    T3,
707    T4,
708    T5,
709    T6,
710}
711impl RiscVReg {
712    pub fn name(&self) -> &str {
713        match self {
714            RiscVReg::Zero => "zero",
715            RiscVReg::Ra => "ra",
716            RiscVReg::Sp => "sp",
717            RiscVReg::Gp => "gp",
718            RiscVReg::Tp => "tp",
719            RiscVReg::T0 => "t0",
720            RiscVReg::T1 => "t1",
721            RiscVReg::T2 => "t2",
722            RiscVReg::S0 => "s0",
723            RiscVReg::S1 => "s1",
724            RiscVReg::A0 => "a0",
725            RiscVReg::A1 => "a1",
726            RiscVReg::A2 => "a2",
727            RiscVReg::A3 => "a3",
728            RiscVReg::A4 => "a4",
729            RiscVReg::A5 => "a5",
730            RiscVReg::A6 => "a6",
731            RiscVReg::A7 => "a7",
732            RiscVReg::S2 => "s2",
733            RiscVReg::S3 => "s3",
734            RiscVReg::S4 => "s4",
735            RiscVReg::S5 => "s5",
736            RiscVReg::S6 => "s6",
737            RiscVReg::S7 => "s7",
738            RiscVReg::S8 => "s8",
739            RiscVReg::S9 => "s9",
740            RiscVReg::S10 => "s10",
741            RiscVReg::S11 => "s11",
742            RiscVReg::T3 => "t3",
743            RiscVReg::T4 => "t4",
744            RiscVReg::T5 => "t5",
745            RiscVReg::T6 => "t6",
746        }
747    }
748    /// x-register index (x0 .. x31).
749    pub fn index(&self) -> u8 {
750        match self {
751            RiscVReg::Zero => 0,
752            RiscVReg::Ra => 1,
753            RiscVReg::Sp => 2,
754            RiscVReg::Gp => 3,
755            RiscVReg::Tp => 4,
756            RiscVReg::T0 => 5,
757            RiscVReg::T1 => 6,
758            RiscVReg::T2 => 7,
759            RiscVReg::S0 => 8,
760            RiscVReg::S1 => 9,
761            RiscVReg::A0 => 10,
762            RiscVReg::A1 => 11,
763            RiscVReg::A2 => 12,
764            RiscVReg::A3 => 13,
765            RiscVReg::A4 => 14,
766            RiscVReg::A5 => 15,
767            RiscVReg::A6 => 16,
768            RiscVReg::A7 => 17,
769            RiscVReg::S2 => 18,
770            RiscVReg::S3 => 19,
771            RiscVReg::S4 => 20,
772            RiscVReg::S5 => 21,
773            RiscVReg::S6 => 22,
774            RiscVReg::S7 => 23,
775            RiscVReg::S8 => 24,
776            RiscVReg::S9 => 25,
777            RiscVReg::S10 => 26,
778            RiscVReg::S11 => 27,
779            RiscVReg::T3 => 28,
780            RiscVReg::T4 => 29,
781            RiscVReg::T5 => 30,
782            RiscVReg::T6 => 31,
783        }
784    }
785}
786/// Constant folding helper for RISCVExt.
787#[allow(dead_code)]
788#[derive(Debug, Clone, Default)]
789pub struct RISCVExtConstFolder {
790    pub(super) folds: usize,
791    pub(super) failures: usize,
792    pub(super) enabled: bool,
793}
794impl RISCVExtConstFolder {
795    #[allow(dead_code)]
796    pub fn new() -> Self {
797        Self {
798            folds: 0,
799            failures: 0,
800            enabled: true,
801        }
802    }
803    #[allow(dead_code)]
804    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
805        self.folds += 1;
806        a.checked_add(b)
807    }
808    #[allow(dead_code)]
809    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
810        self.folds += 1;
811        a.checked_sub(b)
812    }
813    #[allow(dead_code)]
814    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
815        self.folds += 1;
816        a.checked_mul(b)
817    }
818    #[allow(dead_code)]
819    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
820        if b == 0 {
821            self.failures += 1;
822            None
823        } else {
824            self.folds += 1;
825            a.checked_div(b)
826        }
827    }
828    #[allow(dead_code)]
829    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
830        if b == 0 {
831            self.failures += 1;
832            None
833        } else {
834            self.folds += 1;
835            a.checked_rem(b)
836        }
837    }
838    #[allow(dead_code)]
839    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
840        self.folds += 1;
841        a.checked_neg()
842    }
843    #[allow(dead_code)]
844    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
845        if s >= 64 {
846            self.failures += 1;
847            None
848        } else {
849            self.folds += 1;
850            a.checked_shl(s)
851        }
852    }
853    #[allow(dead_code)]
854    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
855        if s >= 64 {
856            self.failures += 1;
857            None
858        } else {
859            self.folds += 1;
860            a.checked_shr(s)
861        }
862    }
863    #[allow(dead_code)]
864    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
865        self.folds += 1;
866        a & b
867    }
868    #[allow(dead_code)]
869    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
870        self.folds += 1;
871        a | b
872    }
873    #[allow(dead_code)]
874    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
875        self.folds += 1;
876        a ^ b
877    }
878    #[allow(dead_code)]
879    pub fn not_i64(&mut self, a: i64) -> i64 {
880        self.folds += 1;
881        !a
882    }
883    #[allow(dead_code)]
884    pub fn fold_count(&self) -> usize {
885        self.folds
886    }
887    #[allow(dead_code)]
888    pub fn failure_count(&self) -> usize {
889        self.failures
890    }
891    #[allow(dead_code)]
892    pub fn enable(&mut self) {
893        self.enabled = true;
894    }
895    #[allow(dead_code)]
896    pub fn disable(&mut self) {
897        self.enabled = false;
898    }
899    #[allow(dead_code)]
900    pub fn is_enabled(&self) -> bool {
901        self.enabled
902    }
903}
904#[allow(dead_code)]
905#[derive(Debug, Clone)]
906pub struct RvDepGraph {
907    pub(super) nodes: Vec<u32>,
908    pub(super) edges: Vec<(u32, u32)>,
909}
910impl RvDepGraph {
911    #[allow(dead_code)]
912    pub fn new() -> Self {
913        RvDepGraph {
914            nodes: Vec::new(),
915            edges: Vec::new(),
916        }
917    }
918    #[allow(dead_code)]
919    pub fn add_node(&mut self, id: u32) {
920        if !self.nodes.contains(&id) {
921            self.nodes.push(id);
922        }
923    }
924    #[allow(dead_code)]
925    pub fn add_dep(&mut self, dep: u32, dependent: u32) {
926        self.add_node(dep);
927        self.add_node(dependent);
928        self.edges.push((dep, dependent));
929    }
930    #[allow(dead_code)]
931    pub fn dependents_of(&self, node: u32) -> Vec<u32> {
932        self.edges
933            .iter()
934            .filter(|(d, _)| *d == node)
935            .map(|(_, dep)| *dep)
936            .collect()
937    }
938    #[allow(dead_code)]
939    pub fn dependencies_of(&self, node: u32) -> Vec<u32> {
940        self.edges
941            .iter()
942            .filter(|(_, dep)| *dep == node)
943            .map(|(d, _)| *d)
944            .collect()
945    }
946    #[allow(dead_code)]
947    pub fn topological_sort(&self) -> Vec<u32> {
948        let mut in_degree: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
949        for &n in &self.nodes {
950            in_degree.insert(n, 0);
951        }
952        for (_, dep) in &self.edges {
953            *in_degree.entry(*dep).or_insert(0) += 1;
954        }
955        let mut queue: std::collections::VecDeque<u32> = self
956            .nodes
957            .iter()
958            .filter(|&&n| in_degree[&n] == 0)
959            .copied()
960            .collect();
961        let mut result = Vec::new();
962        while let Some(node) = queue.pop_front() {
963            result.push(node);
964            for dep in self.dependents_of(node) {
965                let cnt = in_degree.entry(dep).or_insert(0);
966                *cnt = cnt.saturating_sub(1);
967                if *cnt == 0 {
968                    queue.push_back(dep);
969                }
970            }
971        }
972        result
973    }
974    #[allow(dead_code)]
975    pub fn has_cycle(&self) -> bool {
976        self.topological_sort().len() < self.nodes.len()
977    }
978}
979#[allow(dead_code)]
980#[derive(Debug, Clone)]
981pub struct RvAnalysisCache {
982    pub(super) entries: std::collections::HashMap<String, RvCacheEntry>,
983    pub(super) max_size: usize,
984    pub(super) hits: u64,
985    pub(super) misses: u64,
986}
987impl RvAnalysisCache {
988    #[allow(dead_code)]
989    pub fn new(max_size: usize) -> Self {
990        RvAnalysisCache {
991            entries: std::collections::HashMap::new(),
992            max_size,
993            hits: 0,
994            misses: 0,
995        }
996    }
997    #[allow(dead_code)]
998    pub fn get(&mut self, key: &str) -> Option<&RvCacheEntry> {
999        if self.entries.contains_key(key) {
1000            self.hits += 1;
1001            self.entries.get(key)
1002        } else {
1003            self.misses += 1;
1004            None
1005        }
1006    }
1007    #[allow(dead_code)]
1008    pub fn insert(&mut self, key: String, data: Vec<u8>) {
1009        if self.entries.len() >= self.max_size {
1010            if let Some(oldest) = self.entries.keys().next().cloned() {
1011                self.entries.remove(&oldest);
1012            }
1013        }
1014        self.entries.insert(
1015            key.clone(),
1016            RvCacheEntry {
1017                key,
1018                data,
1019                timestamp: 0,
1020                valid: true,
1021            },
1022        );
1023    }
1024    #[allow(dead_code)]
1025    pub fn invalidate(&mut self, key: &str) {
1026        if let Some(entry) = self.entries.get_mut(key) {
1027            entry.valid = false;
1028        }
1029    }
1030    #[allow(dead_code)]
1031    pub fn clear(&mut self) {
1032        self.entries.clear();
1033    }
1034    #[allow(dead_code)]
1035    pub fn hit_rate(&self) -> f64 {
1036        let total = self.hits + self.misses;
1037        if total == 0 {
1038            return 0.0;
1039        }
1040        self.hits as f64 / total as f64
1041    }
1042    #[allow(dead_code)]
1043    pub fn size(&self) -> usize {
1044        self.entries.len()
1045    }
1046}
1047/// Constant folding helper for RISCVX2.
1048#[allow(dead_code)]
1049#[derive(Debug, Clone, Default)]
1050pub struct RISCVX2ConstFolder {
1051    pub(super) folds: usize,
1052    pub(super) failures: usize,
1053    pub(super) enabled: bool,
1054}
1055impl RISCVX2ConstFolder {
1056    #[allow(dead_code)]
1057    pub fn new() -> Self {
1058        Self {
1059            folds: 0,
1060            failures: 0,
1061            enabled: true,
1062        }
1063    }
1064    #[allow(dead_code)]
1065    pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1066        self.folds += 1;
1067        a.checked_add(b)
1068    }
1069    #[allow(dead_code)]
1070    pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1071        self.folds += 1;
1072        a.checked_sub(b)
1073    }
1074    #[allow(dead_code)]
1075    pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1076        self.folds += 1;
1077        a.checked_mul(b)
1078    }
1079    #[allow(dead_code)]
1080    pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1081        if b == 0 {
1082            self.failures += 1;
1083            None
1084        } else {
1085            self.folds += 1;
1086            a.checked_div(b)
1087        }
1088    }
1089    #[allow(dead_code)]
1090    pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
1091        if b == 0 {
1092            self.failures += 1;
1093            None
1094        } else {
1095            self.folds += 1;
1096            a.checked_rem(b)
1097        }
1098    }
1099    #[allow(dead_code)]
1100    pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
1101        self.folds += 1;
1102        a.checked_neg()
1103    }
1104    #[allow(dead_code)]
1105    pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1106        if s >= 64 {
1107            self.failures += 1;
1108            None
1109        } else {
1110            self.folds += 1;
1111            a.checked_shl(s)
1112        }
1113    }
1114    #[allow(dead_code)]
1115    pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
1116        if s >= 64 {
1117            self.failures += 1;
1118            None
1119        } else {
1120            self.folds += 1;
1121            a.checked_shr(s)
1122        }
1123    }
1124    #[allow(dead_code)]
1125    pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
1126        self.folds += 1;
1127        a & b
1128    }
1129    #[allow(dead_code)]
1130    pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
1131        self.folds += 1;
1132        a | b
1133    }
1134    #[allow(dead_code)]
1135    pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
1136        self.folds += 1;
1137        a ^ b
1138    }
1139    #[allow(dead_code)]
1140    pub fn not_i64(&mut self, a: i64) -> i64 {
1141        self.folds += 1;
1142        !a
1143    }
1144    #[allow(dead_code)]
1145    pub fn fold_count(&self) -> usize {
1146        self.folds
1147    }
1148    #[allow(dead_code)]
1149    pub fn failure_count(&self) -> usize {
1150        self.failures
1151    }
1152    #[allow(dead_code)]
1153    pub fn enable(&mut self) {
1154        self.enabled = true;
1155    }
1156    #[allow(dead_code)]
1157    pub fn disable(&mut self) {
1158        self.enabled = false;
1159    }
1160    #[allow(dead_code)]
1161    pub fn is_enabled(&self) -> bool {
1162        self.enabled
1163    }
1164}
1165/// Statistics for RISCVExt passes.
1166#[allow(dead_code)]
1167#[derive(Debug, Clone, Default)]
1168pub struct RISCVExtPassStats {
1169    pub iterations: usize,
1170    pub changed: bool,
1171    pub nodes_visited: usize,
1172    pub nodes_modified: usize,
1173    pub time_ms: u64,
1174    pub memory_bytes: usize,
1175    pub errors: usize,
1176}
1177impl RISCVExtPassStats {
1178    #[allow(dead_code)]
1179    pub fn new() -> Self {
1180        Self::default()
1181    }
1182    #[allow(dead_code)]
1183    pub fn visit(&mut self) {
1184        self.nodes_visited += 1;
1185    }
1186    #[allow(dead_code)]
1187    pub fn modify(&mut self) {
1188        self.nodes_modified += 1;
1189        self.changed = true;
1190    }
1191    #[allow(dead_code)]
1192    pub fn iterate(&mut self) {
1193        self.iterations += 1;
1194    }
1195    #[allow(dead_code)]
1196    pub fn error(&mut self) {
1197        self.errors += 1;
1198    }
1199    #[allow(dead_code)]
1200    pub fn efficiency(&self) -> f64 {
1201        if self.nodes_visited == 0 {
1202            0.0
1203        } else {
1204            self.nodes_modified as f64 / self.nodes_visited as f64
1205        }
1206    }
1207    #[allow(dead_code)]
1208    pub fn merge(&mut self, o: &RISCVExtPassStats) {
1209        self.iterations += o.iterations;
1210        self.changed |= o.changed;
1211        self.nodes_visited += o.nodes_visited;
1212        self.nodes_modified += o.nodes_modified;
1213        self.time_ms += o.time_ms;
1214        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1215        self.errors += o.errors;
1216    }
1217}
1218/// RISC-V instructions (RV32I/RV64I base subset).
1219#[derive(Debug, Clone)]
1220pub enum RiscVInstr {
1221    LUI(RiscVReg, u32),
1222    AUIPC(RiscVReg, u32),
1223    JAL(RiscVReg, i32),
1224    JALR(RiscVReg, RiscVReg, i16),
1225    BEQ(RiscVReg, RiscVReg, i16),
1226    BNE(RiscVReg, RiscVReg, i16),
1227    BLT(RiscVReg, RiscVReg, i16),
1228    BGE(RiscVReg, RiscVReg, i16),
1229    BLTU(RiscVReg, RiscVReg, i16),
1230    BGEU(RiscVReg, RiscVReg, i16),
1231    LB(RiscVReg, RiscVReg, i16),
1232    LH(RiscVReg, RiscVReg, i16),
1233    LW(RiscVReg, RiscVReg, i16),
1234    LBU(RiscVReg, RiscVReg, i16),
1235    LHU(RiscVReg, RiscVReg, i16),
1236    LD(RiscVReg, RiscVReg, i16),
1237    LWU(RiscVReg, RiscVReg, i16),
1238    SB(RiscVReg, RiscVReg, i16),
1239    SH(RiscVReg, RiscVReg, i16),
1240    SW(RiscVReg, RiscVReg, i16),
1241    SD(RiscVReg, RiscVReg, i16),
1242    ADDI(RiscVReg, RiscVReg, i16),
1243    SLTI(RiscVReg, RiscVReg, i16),
1244    SLTIU(RiscVReg, RiscVReg, i16),
1245    XORI(RiscVReg, RiscVReg, i16),
1246    ORI(RiscVReg, RiscVReg, i16),
1247    ANDI(RiscVReg, RiscVReg, i16),
1248    SLLI(RiscVReg, RiscVReg, u8),
1249    SRLI(RiscVReg, RiscVReg, u8),
1250    SRAI(RiscVReg, RiscVReg, u8),
1251    ADDIW(RiscVReg, RiscVReg, i16),
1252    ADD(RiscVReg, RiscVReg, RiscVReg),
1253    SUB(RiscVReg, RiscVReg, RiscVReg),
1254    SLL(RiscVReg, RiscVReg, RiscVReg),
1255    SLT(RiscVReg, RiscVReg, RiscVReg),
1256    SLTU(RiscVReg, RiscVReg, RiscVReg),
1257    XOR(RiscVReg, RiscVReg, RiscVReg),
1258    SRL(RiscVReg, RiscVReg, RiscVReg),
1259    SRA(RiscVReg, RiscVReg, RiscVReg),
1260    OR(RiscVReg, RiscVReg, RiscVReg),
1261    AND(RiscVReg, RiscVReg, RiscVReg),
1262    MUL(RiscVReg, RiscVReg, RiscVReg),
1263    MULH(RiscVReg, RiscVReg, RiscVReg),
1264    DIV(RiscVReg, RiscVReg, RiscVReg),
1265    REM(RiscVReg, RiscVReg, RiscVReg),
1266    ECALL,
1267    EBREAK,
1268    Label(String),
1269    Directive(String, String),
1270    /// Pseudo-instruction: load immediate (expanded by assembler).
1271    LI(RiscVReg, i64),
1272    /// Pseudo-instruction: move.
1273    MV(RiscVReg, RiscVReg),
1274    /// Pseudo-instruction: no-op.
1275    NOP,
1276    /// Pseudo-instruction: return (jalr zero, ra, 0).
1277    RET,
1278    /// Pseudo-instruction: call function by name.
1279    CALL(String),
1280}
1281/// Pass registry for RISCVX2.
1282#[allow(dead_code)]
1283#[derive(Debug, Default)]
1284pub struct RISCVX2PassRegistry {
1285    pub(super) configs: Vec<RISCVX2PassConfig>,
1286    pub(super) stats: Vec<RISCVX2PassStats>,
1287}
1288impl RISCVX2PassRegistry {
1289    #[allow(dead_code)]
1290    pub fn new() -> Self {
1291        Self::default()
1292    }
1293    #[allow(dead_code)]
1294    pub fn register(&mut self, c: RISCVX2PassConfig) {
1295        self.stats.push(RISCVX2PassStats::new());
1296        self.configs.push(c);
1297    }
1298    #[allow(dead_code)]
1299    pub fn len(&self) -> usize {
1300        self.configs.len()
1301    }
1302    #[allow(dead_code)]
1303    pub fn is_empty(&self) -> bool {
1304        self.configs.is_empty()
1305    }
1306    #[allow(dead_code)]
1307    pub fn get(&self, i: usize) -> Option<&RISCVX2PassConfig> {
1308        self.configs.get(i)
1309    }
1310    #[allow(dead_code)]
1311    pub fn get_stats(&self, i: usize) -> Option<&RISCVX2PassStats> {
1312        self.stats.get(i)
1313    }
1314    #[allow(dead_code)]
1315    pub fn enabled_passes(&self) -> Vec<&RISCVX2PassConfig> {
1316        self.configs.iter().filter(|c| c.enabled).collect()
1317    }
1318    #[allow(dead_code)]
1319    pub fn passes_in_phase(&self, ph: &RISCVX2PassPhase) -> Vec<&RISCVX2PassConfig> {
1320        self.configs
1321            .iter()
1322            .filter(|c| c.enabled && &c.phase == ph)
1323            .collect()
1324    }
1325    #[allow(dead_code)]
1326    pub fn total_nodes_visited(&self) -> usize {
1327        self.stats.iter().map(|s| s.nodes_visited).sum()
1328    }
1329    #[allow(dead_code)]
1330    pub fn any_changed(&self) -> bool {
1331        self.stats.iter().any(|s| s.changed)
1332    }
1333}
1334#[allow(dead_code)]
1335#[derive(Debug, Clone, PartialEq)]
1336pub enum RvPassPhase {
1337    Analysis,
1338    Transformation,
1339    Verification,
1340    Cleanup,
1341}
1342impl RvPassPhase {
1343    #[allow(dead_code)]
1344    pub fn name(&self) -> &str {
1345        match self {
1346            RvPassPhase::Analysis => "analysis",
1347            RvPassPhase::Transformation => "transformation",
1348            RvPassPhase::Verification => "verification",
1349            RvPassPhase::Cleanup => "cleanup",
1350        }
1351    }
1352    #[allow(dead_code)]
1353    pub fn is_modifying(&self) -> bool {
1354        matches!(self, RvPassPhase::Transformation | RvPassPhase::Cleanup)
1355    }
1356}
1357/// Pass execution phase for RISCVX2.
1358#[allow(dead_code)]
1359#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1360pub enum RISCVX2PassPhase {
1361    Early,
1362    Middle,
1363    Late,
1364    Finalize,
1365}
1366impl RISCVX2PassPhase {
1367    #[allow(dead_code)]
1368    pub fn is_early(&self) -> bool {
1369        matches!(self, Self::Early)
1370    }
1371    #[allow(dead_code)]
1372    pub fn is_middle(&self) -> bool {
1373        matches!(self, Self::Middle)
1374    }
1375    #[allow(dead_code)]
1376    pub fn is_late(&self) -> bool {
1377        matches!(self, Self::Late)
1378    }
1379    #[allow(dead_code)]
1380    pub fn is_finalize(&self) -> bool {
1381        matches!(self, Self::Finalize)
1382    }
1383    #[allow(dead_code)]
1384    pub fn order(&self) -> u32 {
1385        match self {
1386            Self::Early => 0,
1387            Self::Middle => 1,
1388            Self::Late => 2,
1389            Self::Finalize => 3,
1390        }
1391    }
1392    #[allow(dead_code)]
1393    pub fn from_order(n: u32) -> Option<Self> {
1394        match n {
1395            0 => Some(Self::Early),
1396            1 => Some(Self::Middle),
1397            2 => Some(Self::Late),
1398            3 => Some(Self::Finalize),
1399            _ => None,
1400        }
1401    }
1402}
1403/// Dominator tree for RISCVX2.
1404#[allow(dead_code)]
1405#[derive(Debug, Clone)]
1406pub struct RISCVX2DomTree {
1407    pub(super) idom: Vec<Option<usize>>,
1408    pub(super) children: Vec<Vec<usize>>,
1409    pub(super) depth: Vec<usize>,
1410}
1411impl RISCVX2DomTree {
1412    #[allow(dead_code)]
1413    pub fn new(n: usize) -> Self {
1414        Self {
1415            idom: vec![None; n],
1416            children: vec![Vec::new(); n],
1417            depth: vec![0; n],
1418        }
1419    }
1420    #[allow(dead_code)]
1421    pub fn set_idom(&mut self, node: usize, dom: usize) {
1422        if node < self.idom.len() {
1423            self.idom[node] = Some(dom);
1424            if dom < self.children.len() {
1425                self.children[dom].push(node);
1426            }
1427            self.depth[node] = if dom < self.depth.len() {
1428                self.depth[dom] + 1
1429            } else {
1430                1
1431            };
1432        }
1433    }
1434    #[allow(dead_code)]
1435    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1436        if a == b {
1437            return true;
1438        }
1439        let n = self.idom.len();
1440        for _ in 0..n {
1441            match self.idom.get(b).copied().flatten() {
1442                None => return false,
1443                Some(p) if p == a => return true,
1444                Some(p) if p == b => return false,
1445                Some(p) => b = p,
1446            }
1447        }
1448        false
1449    }
1450    #[allow(dead_code)]
1451    pub fn children_of(&self, n: usize) -> &[usize] {
1452        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1453    }
1454    #[allow(dead_code)]
1455    pub fn depth_of(&self, n: usize) -> usize {
1456        self.depth.get(n).copied().unwrap_or(0)
1457    }
1458    #[allow(dead_code)]
1459    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1460        let n = self.idom.len();
1461        for _ in 0..(2 * n) {
1462            if a == b {
1463                return a;
1464            }
1465            if self.depth_of(a) > self.depth_of(b) {
1466                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1467            } else {
1468                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1469            }
1470        }
1471        0
1472    }
1473}
1474/// Configuration for RISCVExt passes.
1475#[allow(dead_code)]
1476#[derive(Debug, Clone)]
1477pub struct RISCVExtPassConfig {
1478    pub name: String,
1479    pub phase: RISCVExtPassPhase,
1480    pub enabled: bool,
1481    pub max_iterations: usize,
1482    pub debug: u32,
1483    pub timeout_ms: Option<u64>,
1484}
1485impl RISCVExtPassConfig {
1486    #[allow(dead_code)]
1487    pub fn new(name: impl Into<String>) -> Self {
1488        Self {
1489            name: name.into(),
1490            phase: RISCVExtPassPhase::Middle,
1491            enabled: true,
1492            max_iterations: 100,
1493            debug: 0,
1494            timeout_ms: None,
1495        }
1496    }
1497    #[allow(dead_code)]
1498    pub fn with_phase(mut self, phase: RISCVExtPassPhase) -> Self {
1499        self.phase = phase;
1500        self
1501    }
1502    #[allow(dead_code)]
1503    pub fn with_max_iter(mut self, n: usize) -> Self {
1504        self.max_iterations = n;
1505        self
1506    }
1507    #[allow(dead_code)]
1508    pub fn with_debug(mut self, d: u32) -> Self {
1509        self.debug = d;
1510        self
1511    }
1512    #[allow(dead_code)]
1513    pub fn disabled(mut self) -> Self {
1514        self.enabled = false;
1515        self
1516    }
1517    #[allow(dead_code)]
1518    pub fn with_timeout(mut self, ms: u64) -> Self {
1519        self.timeout_ms = Some(ms);
1520        self
1521    }
1522    #[allow(dead_code)]
1523    pub fn is_debug_enabled(&self) -> bool {
1524        self.debug > 0
1525    }
1526}
1527#[allow(dead_code)]
1528#[derive(Debug, Clone)]
1529pub struct RvPassConfig {
1530    pub phase: RvPassPhase,
1531    pub enabled: bool,
1532    pub max_iterations: u32,
1533    pub debug_output: bool,
1534    pub pass_name: String,
1535}
1536impl RvPassConfig {
1537    #[allow(dead_code)]
1538    pub fn new(name: impl Into<String>, phase: RvPassPhase) -> Self {
1539        RvPassConfig {
1540            phase,
1541            enabled: true,
1542            max_iterations: 10,
1543            debug_output: false,
1544            pass_name: name.into(),
1545        }
1546    }
1547    #[allow(dead_code)]
1548    pub fn disabled(mut self) -> Self {
1549        self.enabled = false;
1550        self
1551    }
1552    #[allow(dead_code)]
1553    pub fn with_debug(mut self) -> Self {
1554        self.debug_output = true;
1555        self
1556    }
1557    #[allow(dead_code)]
1558    pub fn max_iter(mut self, n: u32) -> Self {
1559        self.max_iterations = n;
1560        self
1561    }
1562}
1563/// Worklist for RISCVExt.
1564#[allow(dead_code)]
1565#[derive(Debug, Clone)]
1566pub struct RISCVExtWorklist {
1567    pub(super) items: std::collections::VecDeque<usize>,
1568    pub(super) present: Vec<bool>,
1569}
1570impl RISCVExtWorklist {
1571    #[allow(dead_code)]
1572    pub fn new(capacity: usize) -> Self {
1573        Self {
1574            items: std::collections::VecDeque::new(),
1575            present: vec![false; capacity],
1576        }
1577    }
1578    #[allow(dead_code)]
1579    pub fn push(&mut self, id: usize) {
1580        if id < self.present.len() && !self.present[id] {
1581            self.present[id] = true;
1582            self.items.push_back(id);
1583        }
1584    }
1585    #[allow(dead_code)]
1586    pub fn push_front(&mut self, id: usize) {
1587        if id < self.present.len() && !self.present[id] {
1588            self.present[id] = true;
1589            self.items.push_front(id);
1590        }
1591    }
1592    #[allow(dead_code)]
1593    pub fn pop(&mut self) -> Option<usize> {
1594        let id = self.items.pop_front()?;
1595        if id < self.present.len() {
1596            self.present[id] = false;
1597        }
1598        Some(id)
1599    }
1600    #[allow(dead_code)]
1601    pub fn is_empty(&self) -> bool {
1602        self.items.is_empty()
1603    }
1604    #[allow(dead_code)]
1605    pub fn len(&self) -> usize {
1606        self.items.len()
1607    }
1608    #[allow(dead_code)]
1609    pub fn contains(&self, id: usize) -> bool {
1610        id < self.present.len() && self.present[id]
1611    }
1612    #[allow(dead_code)]
1613    pub fn drain_all(&mut self) -> Vec<usize> {
1614        let v: Vec<usize> = self.items.drain(..).collect();
1615        for &id in &v {
1616            if id < self.present.len() {
1617                self.present[id] = false;
1618            }
1619        }
1620        v
1621    }
1622}
1623/// Dominator tree for RISCVExt.
1624#[allow(dead_code)]
1625#[derive(Debug, Clone)]
1626pub struct RISCVExtDomTree {
1627    pub(super) idom: Vec<Option<usize>>,
1628    pub(super) children: Vec<Vec<usize>>,
1629    pub(super) depth: Vec<usize>,
1630}
1631impl RISCVExtDomTree {
1632    #[allow(dead_code)]
1633    pub fn new(n: usize) -> Self {
1634        Self {
1635            idom: vec![None; n],
1636            children: vec![Vec::new(); n],
1637            depth: vec![0; n],
1638        }
1639    }
1640    #[allow(dead_code)]
1641    pub fn set_idom(&mut self, node: usize, dom: usize) {
1642        if node < self.idom.len() {
1643            self.idom[node] = Some(dom);
1644            if dom < self.children.len() {
1645                self.children[dom].push(node);
1646            }
1647            self.depth[node] = if dom < self.depth.len() {
1648                self.depth[dom] + 1
1649            } else {
1650                1
1651            };
1652        }
1653    }
1654    #[allow(dead_code)]
1655    pub fn dominates(&self, a: usize, mut b: usize) -> bool {
1656        if a == b {
1657            return true;
1658        }
1659        let n = self.idom.len();
1660        for _ in 0..n {
1661            match self.idom.get(b).copied().flatten() {
1662                None => return false,
1663                Some(p) if p == a => return true,
1664                Some(p) if p == b => return false,
1665                Some(p) => b = p,
1666            }
1667        }
1668        false
1669    }
1670    #[allow(dead_code)]
1671    pub fn children_of(&self, n: usize) -> &[usize] {
1672        self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
1673    }
1674    #[allow(dead_code)]
1675    pub fn depth_of(&self, n: usize) -> usize {
1676        self.depth.get(n).copied().unwrap_or(0)
1677    }
1678    #[allow(dead_code)]
1679    pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
1680        let n = self.idom.len();
1681        for _ in 0..(2 * n) {
1682            if a == b {
1683                return a;
1684            }
1685            if self.depth_of(a) > self.depth_of(b) {
1686                a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
1687            } else {
1688                b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
1689            }
1690        }
1691        0
1692    }
1693}
1694#[allow(dead_code)]
1695#[derive(Debug, Clone)]
1696pub struct RvWorklist {
1697    pub(super) items: std::collections::VecDeque<u32>,
1698    pub(super) in_worklist: std::collections::HashSet<u32>,
1699}
1700impl RvWorklist {
1701    #[allow(dead_code)]
1702    pub fn new() -> Self {
1703        RvWorklist {
1704            items: std::collections::VecDeque::new(),
1705            in_worklist: std::collections::HashSet::new(),
1706        }
1707    }
1708    #[allow(dead_code)]
1709    pub fn push(&mut self, item: u32) -> bool {
1710        if self.in_worklist.insert(item) {
1711            self.items.push_back(item);
1712            true
1713        } else {
1714            false
1715        }
1716    }
1717    #[allow(dead_code)]
1718    pub fn pop(&mut self) -> Option<u32> {
1719        let item = self.items.pop_front()?;
1720        self.in_worklist.remove(&item);
1721        Some(item)
1722    }
1723    #[allow(dead_code)]
1724    pub fn is_empty(&self) -> bool {
1725        self.items.is_empty()
1726    }
1727    #[allow(dead_code)]
1728    pub fn len(&self) -> usize {
1729        self.items.len()
1730    }
1731    #[allow(dead_code)]
1732    pub fn contains(&self, item: u32) -> bool {
1733        self.in_worklist.contains(&item)
1734    }
1735}
1736#[allow(dead_code)]
1737#[derive(Debug, Clone, Default)]
1738pub struct RvPassStats {
1739    pub total_runs: u32,
1740    pub successful_runs: u32,
1741    pub total_changes: u64,
1742    pub time_ms: u64,
1743    pub iterations_used: u32,
1744}
1745impl RvPassStats {
1746    #[allow(dead_code)]
1747    pub fn new() -> Self {
1748        Self::default()
1749    }
1750    #[allow(dead_code)]
1751    pub fn record_run(&mut self, changes: u64, time_ms: u64, iterations: u32) {
1752        self.total_runs += 1;
1753        self.successful_runs += 1;
1754        self.total_changes += changes;
1755        self.time_ms += time_ms;
1756        self.iterations_used = iterations;
1757    }
1758    #[allow(dead_code)]
1759    pub fn average_changes_per_run(&self) -> f64 {
1760        if self.total_runs == 0 {
1761            return 0.0;
1762        }
1763        self.total_changes as f64 / self.total_runs as f64
1764    }
1765    #[allow(dead_code)]
1766    pub fn success_rate(&self) -> f64 {
1767        if self.total_runs == 0 {
1768            return 0.0;
1769        }
1770        self.successful_runs as f64 / self.total_runs as f64
1771    }
1772    #[allow(dead_code)]
1773    pub fn format_summary(&self) -> String {
1774        format!(
1775            "Runs: {}/{}, Changes: {}, Time: {}ms",
1776            self.successful_runs, self.total_runs, self.total_changes, self.time_ms
1777        )
1778    }
1779}
1780/// Pass execution phase for RISCVExt.
1781#[allow(dead_code)]
1782#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1783pub enum RISCVExtPassPhase {
1784    Early,
1785    Middle,
1786    Late,
1787    Finalize,
1788}
1789impl RISCVExtPassPhase {
1790    #[allow(dead_code)]
1791    pub fn is_early(&self) -> bool {
1792        matches!(self, Self::Early)
1793    }
1794    #[allow(dead_code)]
1795    pub fn is_middle(&self) -> bool {
1796        matches!(self, Self::Middle)
1797    }
1798    #[allow(dead_code)]
1799    pub fn is_late(&self) -> bool {
1800        matches!(self, Self::Late)
1801    }
1802    #[allow(dead_code)]
1803    pub fn is_finalize(&self) -> bool {
1804        matches!(self, Self::Finalize)
1805    }
1806    #[allow(dead_code)]
1807    pub fn order(&self) -> u32 {
1808        match self {
1809            Self::Early => 0,
1810            Self::Middle => 1,
1811            Self::Late => 2,
1812            Self::Finalize => 3,
1813        }
1814    }
1815    #[allow(dead_code)]
1816    pub fn from_order(n: u32) -> Option<Self> {
1817        match n {
1818            0 => Some(Self::Early),
1819            1 => Some(Self::Middle),
1820            2 => Some(Self::Late),
1821            3 => Some(Self::Finalize),
1822            _ => None,
1823        }
1824    }
1825}
1826/// Statistics for RISCVX2 passes.
1827#[allow(dead_code)]
1828#[derive(Debug, Clone, Default)]
1829pub struct RISCVX2PassStats {
1830    pub iterations: usize,
1831    pub changed: bool,
1832    pub nodes_visited: usize,
1833    pub nodes_modified: usize,
1834    pub time_ms: u64,
1835    pub memory_bytes: usize,
1836    pub errors: usize,
1837}
1838impl RISCVX2PassStats {
1839    #[allow(dead_code)]
1840    pub fn new() -> Self {
1841        Self::default()
1842    }
1843    #[allow(dead_code)]
1844    pub fn visit(&mut self) {
1845        self.nodes_visited += 1;
1846    }
1847    #[allow(dead_code)]
1848    pub fn modify(&mut self) {
1849        self.nodes_modified += 1;
1850        self.changed = true;
1851    }
1852    #[allow(dead_code)]
1853    pub fn iterate(&mut self) {
1854        self.iterations += 1;
1855    }
1856    #[allow(dead_code)]
1857    pub fn error(&mut self) {
1858        self.errors += 1;
1859    }
1860    #[allow(dead_code)]
1861    pub fn efficiency(&self) -> f64 {
1862        if self.nodes_visited == 0 {
1863            0.0
1864        } else {
1865            self.nodes_modified as f64 / self.nodes_visited as f64
1866        }
1867    }
1868    #[allow(dead_code)]
1869    pub fn merge(&mut self, o: &RISCVX2PassStats) {
1870        self.iterations += o.iterations;
1871        self.changed |= o.changed;
1872        self.nodes_visited += o.nodes_visited;
1873        self.nodes_modified += o.nodes_modified;
1874        self.time_ms += o.time_ms;
1875        self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
1876        self.errors += o.errors;
1877    }
1878}
1879/// A named RISC-V function / code section.
1880#[derive(Debug, Clone)]
1881pub struct RiscVFunction {
1882    pub name: String,
1883    pub instrs: Vec<RiscVInstr>,
1884}
1885impl RiscVFunction {
1886    pub fn new(name: impl Into<String>) -> Self {
1887        Self {
1888            name: name.into(),
1889            instrs: Vec::new(),
1890        }
1891    }
1892    pub fn push(&mut self, instr: RiscVInstr) {
1893        self.instrs.push(instr);
1894    }
1895}
1896/// Configuration for RISCVX2 passes.
1897#[allow(dead_code)]
1898#[derive(Debug, Clone)]
1899pub struct RISCVX2PassConfig {
1900    pub name: String,
1901    pub phase: RISCVX2PassPhase,
1902    pub enabled: bool,
1903    pub max_iterations: usize,
1904    pub debug: u32,
1905    pub timeout_ms: Option<u64>,
1906}
1907impl RISCVX2PassConfig {
1908    #[allow(dead_code)]
1909    pub fn new(name: impl Into<String>) -> Self {
1910        Self {
1911            name: name.into(),
1912            phase: RISCVX2PassPhase::Middle,
1913            enabled: true,
1914            max_iterations: 100,
1915            debug: 0,
1916            timeout_ms: None,
1917        }
1918    }
1919    #[allow(dead_code)]
1920    pub fn with_phase(mut self, phase: RISCVX2PassPhase) -> Self {
1921        self.phase = phase;
1922        self
1923    }
1924    #[allow(dead_code)]
1925    pub fn with_max_iter(mut self, n: usize) -> Self {
1926        self.max_iterations = n;
1927        self
1928    }
1929    #[allow(dead_code)]
1930    pub fn with_debug(mut self, d: u32) -> Self {
1931        self.debug = d;
1932        self
1933    }
1934    #[allow(dead_code)]
1935    pub fn disabled(mut self) -> Self {
1936        self.enabled = false;
1937        self
1938    }
1939    #[allow(dead_code)]
1940    pub fn with_timeout(mut self, ms: u64) -> Self {
1941        self.timeout_ms = Some(ms);
1942        self
1943    }
1944    #[allow(dead_code)]
1945    pub fn is_debug_enabled(&self) -> bool {
1946        self.debug > 0
1947    }
1948}
1949#[allow(dead_code)]
1950#[derive(Debug, Clone)]
1951pub struct RvCacheEntry {
1952    pub key: String,
1953    pub data: Vec<u8>,
1954    pub timestamp: u64,
1955    pub valid: bool,
1956}
1957#[allow(dead_code)]
1958pub struct RvConstantFoldingHelper;
1959impl RvConstantFoldingHelper {
1960    #[allow(dead_code)]
1961    pub fn fold_add_i64(a: i64, b: i64) -> Option<i64> {
1962        a.checked_add(b)
1963    }
1964    #[allow(dead_code)]
1965    pub fn fold_sub_i64(a: i64, b: i64) -> Option<i64> {
1966        a.checked_sub(b)
1967    }
1968    #[allow(dead_code)]
1969    pub fn fold_mul_i64(a: i64, b: i64) -> Option<i64> {
1970        a.checked_mul(b)
1971    }
1972    #[allow(dead_code)]
1973    pub fn fold_div_i64(a: i64, b: i64) -> Option<i64> {
1974        if b == 0 {
1975            None
1976        } else {
1977            a.checked_div(b)
1978        }
1979    }
1980    #[allow(dead_code)]
1981    pub fn fold_add_f64(a: f64, b: f64) -> f64 {
1982        a + b
1983    }
1984    #[allow(dead_code)]
1985    pub fn fold_mul_f64(a: f64, b: f64) -> f64 {
1986        a * b
1987    }
1988    #[allow(dead_code)]
1989    pub fn fold_neg_i64(a: i64) -> Option<i64> {
1990        a.checked_neg()
1991    }
1992    #[allow(dead_code)]
1993    pub fn fold_not_bool(a: bool) -> bool {
1994        !a
1995    }
1996    #[allow(dead_code)]
1997    pub fn fold_and_bool(a: bool, b: bool) -> bool {
1998        a && b
1999    }
2000    #[allow(dead_code)]
2001    pub fn fold_or_bool(a: bool, b: bool) -> bool {
2002        a || b
2003    }
2004    #[allow(dead_code)]
2005    pub fn fold_shl_i64(a: i64, b: u32) -> Option<i64> {
2006        a.checked_shl(b)
2007    }
2008    #[allow(dead_code)]
2009    pub fn fold_shr_i64(a: i64, b: u32) -> Option<i64> {
2010        a.checked_shr(b)
2011    }
2012    #[allow(dead_code)]
2013    pub fn fold_rem_i64(a: i64, b: i64) -> Option<i64> {
2014        if b == 0 {
2015            None
2016        } else {
2017            Some(a % b)
2018        }
2019    }
2020    #[allow(dead_code)]
2021    pub fn fold_bitand_i64(a: i64, b: i64) -> i64 {
2022        a & b
2023    }
2024    #[allow(dead_code)]
2025    pub fn fold_bitor_i64(a: i64, b: i64) -> i64 {
2026        a | b
2027    }
2028    #[allow(dead_code)]
2029    pub fn fold_bitxor_i64(a: i64, b: i64) -> i64 {
2030        a ^ b
2031    }
2032    #[allow(dead_code)]
2033    pub fn fold_bitnot_i64(a: i64) -> i64 {
2034        !a
2035    }
2036}
2037/// Analysis cache for RISCVX2.
2038#[allow(dead_code)]
2039#[derive(Debug)]
2040pub struct RISCVX2Cache {
2041    pub(super) entries: Vec<(u64, Vec<u8>, bool, u32)>,
2042    pub(super) cap: usize,
2043    pub(super) total_hits: u64,
2044    pub(super) total_misses: u64,
2045}
2046impl RISCVX2Cache {
2047    #[allow(dead_code)]
2048    pub fn new(cap: usize) -> Self {
2049        Self {
2050            entries: Vec::new(),
2051            cap,
2052            total_hits: 0,
2053            total_misses: 0,
2054        }
2055    }
2056    #[allow(dead_code)]
2057    pub fn get(&mut self, key: u64) -> Option<&[u8]> {
2058        for e in self.entries.iter_mut() {
2059            if e.0 == key && e.2 {
2060                e.3 += 1;
2061                self.total_hits += 1;
2062                return Some(&e.1);
2063            }
2064        }
2065        self.total_misses += 1;
2066        None
2067    }
2068    #[allow(dead_code)]
2069    pub fn put(&mut self, key: u64, data: Vec<u8>) {
2070        if self.entries.len() >= self.cap {
2071            self.entries.retain(|e| e.2);
2072            if self.entries.len() >= self.cap {
2073                self.entries.remove(0);
2074            }
2075        }
2076        self.entries.push((key, data, true, 0));
2077    }
2078    #[allow(dead_code)]
2079    pub fn invalidate(&mut self) {
2080        for e in self.entries.iter_mut() {
2081            e.2 = false;
2082        }
2083    }
2084    #[allow(dead_code)]
2085    pub fn hit_rate(&self) -> f64 {
2086        let t = self.total_hits + self.total_misses;
2087        if t == 0 {
2088            0.0
2089        } else {
2090            self.total_hits as f64 / t as f64
2091        }
2092    }
2093    #[allow(dead_code)]
2094    pub fn live_count(&self) -> usize {
2095        self.entries.iter().filter(|e| e.2).count()
2096    }
2097}
2098/// Dependency graph for RISCVExt.
2099#[allow(dead_code)]
2100#[derive(Debug, Clone)]
2101pub struct RISCVExtDepGraph {
2102    pub(super) n: usize,
2103    pub(super) adj: Vec<Vec<usize>>,
2104    pub(super) rev: Vec<Vec<usize>>,
2105    pub(super) edge_count: usize,
2106}
2107impl RISCVExtDepGraph {
2108    #[allow(dead_code)]
2109    pub fn new(n: usize) -> Self {
2110        Self {
2111            n,
2112            adj: vec![Vec::new(); n],
2113            rev: vec![Vec::new(); n],
2114            edge_count: 0,
2115        }
2116    }
2117    #[allow(dead_code)]
2118    pub fn add_edge(&mut self, from: usize, to: usize) {
2119        if from < self.n && to < self.n {
2120            if !self.adj[from].contains(&to) {
2121                self.adj[from].push(to);
2122                self.rev[to].push(from);
2123                self.edge_count += 1;
2124            }
2125        }
2126    }
2127    #[allow(dead_code)]
2128    pub fn succs(&self, n: usize) -> &[usize] {
2129        self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2130    }
2131    #[allow(dead_code)]
2132    pub fn preds(&self, n: usize) -> &[usize] {
2133        self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
2134    }
2135    #[allow(dead_code)]
2136    pub fn topo_sort(&self) -> Option<Vec<usize>> {
2137        let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
2138        let mut q: std::collections::VecDeque<usize> =
2139            (0..self.n).filter(|&i| deg[i] == 0).collect();
2140        let mut out = Vec::with_capacity(self.n);
2141        while let Some(u) = q.pop_front() {
2142            out.push(u);
2143            for &v in &self.adj[u] {
2144                deg[v] -= 1;
2145                if deg[v] == 0 {
2146                    q.push_back(v);
2147                }
2148            }
2149        }
2150        if out.len() == self.n {
2151            Some(out)
2152        } else {
2153            None
2154        }
2155    }
2156    #[allow(dead_code)]
2157    pub fn has_cycle(&self) -> bool {
2158        self.topo_sort().is_none()
2159    }
2160    #[allow(dead_code)]
2161    pub fn reachable(&self, start: usize) -> Vec<usize> {
2162        let mut vis = vec![false; self.n];
2163        let mut stk = vec![start];
2164        let mut out = Vec::new();
2165        while let Some(u) = stk.pop() {
2166            if u < self.n && !vis[u] {
2167                vis[u] = true;
2168                out.push(u);
2169                for &v in &self.adj[u] {
2170                    if !vis[v] {
2171                        stk.push(v);
2172                    }
2173                }
2174            }
2175        }
2176        out
2177    }
2178    #[allow(dead_code)]
2179    pub fn scc(&self) -> Vec<Vec<usize>> {
2180        let mut visited = vec![false; self.n];
2181        let mut order = Vec::new();
2182        for i in 0..self.n {
2183            if !visited[i] {
2184                let mut stk = vec![(i, 0usize)];
2185                while let Some((u, idx)) = stk.last_mut() {
2186                    if !visited[*u] {
2187                        visited[*u] = true;
2188                    }
2189                    if *idx < self.adj[*u].len() {
2190                        let v = self.adj[*u][*idx];
2191                        *idx += 1;
2192                        if !visited[v] {
2193                            stk.push((v, 0));
2194                        }
2195                    } else {
2196                        order.push(*u);
2197                        stk.pop();
2198                    }
2199                }
2200            }
2201        }
2202        let mut comp = vec![usize::MAX; self.n];
2203        let mut components: Vec<Vec<usize>> = Vec::new();
2204        for &start in order.iter().rev() {
2205            if comp[start] == usize::MAX {
2206                let cid = components.len();
2207                let mut component = Vec::new();
2208                let mut stk = vec![start];
2209                while let Some(u) = stk.pop() {
2210                    if comp[u] == usize::MAX {
2211                        comp[u] = cid;
2212                        component.push(u);
2213                        for &v in &self.rev[u] {
2214                            if comp[v] == usize::MAX {
2215                                stk.push(v);
2216                            }
2217                        }
2218                    }
2219                }
2220                components.push(component);
2221            }
2222        }
2223        components
2224    }
2225    #[allow(dead_code)]
2226    pub fn node_count(&self) -> usize {
2227        self.n
2228    }
2229    #[allow(dead_code)]
2230    pub fn edge_count(&self) -> usize {
2231        self.edge_count
2232    }
2233}
2234#[allow(dead_code)]
2235pub struct RvPassRegistry {
2236    pub(super) configs: Vec<RvPassConfig>,
2237    pub(super) stats: std::collections::HashMap<String, RvPassStats>,
2238}
2239impl RvPassRegistry {
2240    #[allow(dead_code)]
2241    pub fn new() -> Self {
2242        RvPassRegistry {
2243            configs: Vec::new(),
2244            stats: std::collections::HashMap::new(),
2245        }
2246    }
2247    #[allow(dead_code)]
2248    pub fn register(&mut self, config: RvPassConfig) {
2249        self.stats
2250            .insert(config.pass_name.clone(), RvPassStats::new());
2251        self.configs.push(config);
2252    }
2253    #[allow(dead_code)]
2254    pub fn enabled_passes(&self) -> Vec<&RvPassConfig> {
2255        self.configs.iter().filter(|c| c.enabled).collect()
2256    }
2257    #[allow(dead_code)]
2258    pub fn get_stats(&self, name: &str) -> Option<&RvPassStats> {
2259        self.stats.get(name)
2260    }
2261    #[allow(dead_code)]
2262    pub fn total_passes(&self) -> usize {
2263        self.configs.len()
2264    }
2265    #[allow(dead_code)]
2266    pub fn enabled_count(&self) -> usize {
2267        self.enabled_passes().len()
2268    }
2269    #[allow(dead_code)]
2270    pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
2271        if let Some(stats) = self.stats.get_mut(name) {
2272            stats.record_run(changes, time_ms, iter);
2273        }
2274    }
2275}
2276#[allow(dead_code)]
2277#[derive(Debug, Clone)]
2278pub struct RvLivenessInfo {
2279    pub live_in: Vec<std::collections::HashSet<u32>>,
2280    pub live_out: Vec<std::collections::HashSet<u32>>,
2281    pub defs: Vec<std::collections::HashSet<u32>>,
2282    pub uses: Vec<std::collections::HashSet<u32>>,
2283}
2284impl RvLivenessInfo {
2285    #[allow(dead_code)]
2286    pub fn new(block_count: usize) -> Self {
2287        RvLivenessInfo {
2288            live_in: vec![std::collections::HashSet::new(); block_count],
2289            live_out: vec![std::collections::HashSet::new(); block_count],
2290            defs: vec![std::collections::HashSet::new(); block_count],
2291            uses: vec![std::collections::HashSet::new(); block_count],
2292        }
2293    }
2294    #[allow(dead_code)]
2295    pub fn add_def(&mut self, block: usize, var: u32) {
2296        if block < self.defs.len() {
2297            self.defs[block].insert(var);
2298        }
2299    }
2300    #[allow(dead_code)]
2301    pub fn add_use(&mut self, block: usize, var: u32) {
2302        if block < self.uses.len() {
2303            self.uses[block].insert(var);
2304        }
2305    }
2306    #[allow(dead_code)]
2307    pub fn is_live_in(&self, block: usize, var: u32) -> bool {
2308        self.live_in
2309            .get(block)
2310            .map(|s| s.contains(&var))
2311            .unwrap_or(false)
2312    }
2313    #[allow(dead_code)]
2314    pub fn is_live_out(&self, block: usize, var: u32) -> bool {
2315        self.live_out
2316            .get(block)
2317            .map(|s| s.contains(&var))
2318            .unwrap_or(false)
2319    }
2320}
2321/// Liveness analysis for RISCVX2.
2322#[allow(dead_code)]
2323#[derive(Debug, Clone, Default)]
2324pub struct RISCVX2Liveness {
2325    pub live_in: Vec<Vec<usize>>,
2326    pub live_out: Vec<Vec<usize>>,
2327    pub defs: Vec<Vec<usize>>,
2328    pub uses: Vec<Vec<usize>>,
2329}
2330impl RISCVX2Liveness {
2331    #[allow(dead_code)]
2332    pub fn new(n: usize) -> Self {
2333        Self {
2334            live_in: vec![Vec::new(); n],
2335            live_out: vec![Vec::new(); n],
2336            defs: vec![Vec::new(); n],
2337            uses: vec![Vec::new(); n],
2338        }
2339    }
2340    #[allow(dead_code)]
2341    pub fn live_in(&self, b: usize, v: usize) -> bool {
2342        self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2343    }
2344    #[allow(dead_code)]
2345    pub fn live_out(&self, b: usize, v: usize) -> bool {
2346        self.live_out
2347            .get(b)
2348            .map(|s| s.contains(&v))
2349            .unwrap_or(false)
2350    }
2351    #[allow(dead_code)]
2352    pub fn add_def(&mut self, b: usize, v: usize) {
2353        if let Some(s) = self.defs.get_mut(b) {
2354            if !s.contains(&v) {
2355                s.push(v);
2356            }
2357        }
2358    }
2359    #[allow(dead_code)]
2360    pub fn add_use(&mut self, b: usize, v: usize) {
2361        if let Some(s) = self.uses.get_mut(b) {
2362            if !s.contains(&v) {
2363                s.push(v);
2364            }
2365        }
2366    }
2367    #[allow(dead_code)]
2368    pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
2369        self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2370    }
2371    #[allow(dead_code)]
2372    pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
2373        self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
2374    }
2375}