use super::defs::*;
use super::impls1::*;
use crate::c_backend::{self, CEmitConfig, COutput};
use crate::closure_convert::{ClosureConvertConfig, ClosureConverter};
use crate::lcnf::*;
use crate::native_backend::{self, NativeEmitConfig, NativeModule};
use crate::opt_dce::{self, DceConfig};
use crate::to_lcnf::{self, ToLcnfConfig};
use crate::CodegenTarget;
use oxilean_kernel::expr::Expr;
use oxilean_kernel::Name;
use super::super::functions::LcnfDeclInput;
use super::super::functions::*;
use std::collections::{HashMap, HashSet, VecDeque};
#[derive(Debug, Clone)]
pub struct PipelineResult {
pub c_output: Option<COutput>,
pub native_output: Option<NativeModule>,
pub lcnf_module: LcnfModule,
pub stats: PipelineStats,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeExtWorklist {
pub(crate) items: std::collections::VecDeque<usize>,
pub(crate) present: Vec<bool>,
}
impl PipeExtWorklist {
#[allow(dead_code)]
pub fn new(capacity: usize) -> Self {
Self {
items: std::collections::VecDeque::new(),
present: vec![false; capacity],
}
}
#[allow(dead_code)]
pub fn push(&mut self, id: usize) {
if id < self.present.len() && !self.present[id] {
self.present[id] = true;
self.items.push_back(id);
}
}
#[allow(dead_code)]
pub fn push_front(&mut self, id: usize) {
if id < self.present.len() && !self.present[id] {
self.present[id] = true;
self.items.push_front(id);
}
}
#[allow(dead_code)]
pub fn pop(&mut self) -> Option<usize> {
let id = self.items.pop_front()?;
if id < self.present.len() {
self.present[id] = false;
}
Some(id)
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.items.len()
}
#[allow(dead_code)]
pub fn contains(&self, id: usize) -> bool {
id < self.present.len() && self.present[id]
}
#[allow(dead_code)]
pub fn drain_all(&mut self) -> Vec<usize> {
let v: Vec<usize> = self.items.drain(..).collect();
for &id in &v {
if id < self.present.len() {
self.present[id] = false;
}
}
v
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct PipeExtLiveness {
pub live_in: Vec<Vec<usize>>,
pub live_out: Vec<Vec<usize>>,
pub defs: Vec<Vec<usize>>,
pub uses: Vec<Vec<usize>>,
}
impl PipeExtLiveness {
#[allow(dead_code)]
pub fn new(n: usize) -> Self {
Self {
live_in: vec![Vec::new(); n],
live_out: vec![Vec::new(); n],
defs: vec![Vec::new(); n],
uses: vec![Vec::new(); n],
}
}
#[allow(dead_code)]
pub fn live_in(&self, b: usize, v: usize) -> bool {
self.live_in.get(b).map(|s| s.contains(&v)).unwrap_or(false)
}
#[allow(dead_code)]
pub fn live_out(&self, b: usize, v: usize) -> bool {
self.live_out
.get(b)
.map(|s| s.contains(&v))
.unwrap_or(false)
}
#[allow(dead_code)]
pub fn add_def(&mut self, b: usize, v: usize) {
if let Some(s) = self.defs.get_mut(b) {
if !s.contains(&v) {
s.push(v);
}
}
}
#[allow(dead_code)]
pub fn add_use(&mut self, b: usize, v: usize) {
if let Some(s) = self.uses.get_mut(b) {
if !s.contains(&v) {
s.push(v);
}
}
}
#[allow(dead_code)]
pub fn var_is_used_in_block(&self, b: usize, v: usize) -> bool {
self.uses.get(b).map(|s| s.contains(&v)).unwrap_or(false)
}
#[allow(dead_code)]
pub fn var_is_def_in_block(&self, b: usize, v: usize) -> bool {
self.defs.get(b).map(|s| s.contains(&v)).unwrap_or(false)
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub struct PipeX2Cache {
pub(crate) entries: Vec<(u64, Vec<u8>, bool, u32)>,
pub(crate) cap: usize,
pub(crate) total_hits: u64,
pub(crate) total_misses: u64,
}
impl PipeX2Cache {
#[allow(dead_code)]
pub fn new(cap: usize) -> Self {
Self {
entries: Vec::new(),
cap,
total_hits: 0,
total_misses: 0,
}
}
#[allow(dead_code)]
pub fn get(&mut self, key: u64) -> Option<&[u8]> {
for e in self.entries.iter_mut() {
if e.0 == key && e.2 {
e.3 += 1;
self.total_hits += 1;
return Some(&e.1);
}
}
self.total_misses += 1;
None
}
#[allow(dead_code)]
pub fn put(&mut self, key: u64, data: Vec<u8>) {
if self.entries.len() >= self.cap {
self.entries.retain(|e| e.2);
if self.entries.len() >= self.cap {
self.entries.remove(0);
}
}
self.entries.push((key, data, true, 0));
}
#[allow(dead_code)]
pub fn invalidate(&mut self) {
for e in self.entries.iter_mut() {
e.2 = false;
}
}
#[allow(dead_code)]
pub fn hit_rate(&self) -> f64 {
let t = self.total_hits + self.total_misses;
if t == 0 {
0.0
} else {
self.total_hits as f64 / t as f64
}
}
#[allow(dead_code)]
pub fn live_count(&self) -> usize {
self.entries.iter().filter(|e| e.2).count()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeLivenessInfo {
pub live_in: Vec<std::collections::HashSet<u32>>,
pub live_out: Vec<std::collections::HashSet<u32>>,
pub defs: Vec<std::collections::HashSet<u32>>,
pub uses: Vec<std::collections::HashSet<u32>>,
}
impl PipeLivenessInfo {
#[allow(dead_code)]
pub fn new(block_count: usize) -> Self {
PipeLivenessInfo {
live_in: vec![std::collections::HashSet::new(); block_count],
live_out: vec![std::collections::HashSet::new(); block_count],
defs: vec![std::collections::HashSet::new(); block_count],
uses: vec![std::collections::HashSet::new(); block_count],
}
}
#[allow(dead_code)]
pub fn add_def(&mut self, block: usize, var: u32) {
if block < self.defs.len() {
self.defs[block].insert(var);
}
}
#[allow(dead_code)]
pub fn add_use(&mut self, block: usize, var: u32) {
if block < self.uses.len() {
self.uses[block].insert(var);
}
}
#[allow(dead_code)]
pub fn is_live_in(&self, block: usize, var: u32) -> bool {
self.live_in
.get(block)
.map(|s| s.contains(&var))
.unwrap_or(false)
}
#[allow(dead_code)]
pub fn is_live_out(&self, block: usize, var: u32) -> bool {
self.live_out
.get(block)
.map(|s| s.contains(&var))
.unwrap_or(false)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum PipePassPhase {
Analysis,
Transformation,
Verification,
Cleanup,
}
impl PipePassPhase {
#[allow(dead_code)]
pub fn name(&self) -> &str {
match self {
PipePassPhase::Analysis => "analysis",
PipePassPhase::Transformation => "transformation",
PipePassPhase::Verification => "verification",
PipePassPhase::Cleanup => "cleanup",
}
}
#[allow(dead_code)]
pub fn is_modifying(&self) -> bool {
matches!(self, PipePassPhase::Transformation | PipePassPhase::Cleanup)
}
}
#[derive(Debug, Clone, Default)]
pub struct PipelineStats {
pub per_pass: Vec<(PassId, PassStats)>,
pub total_time_us: u64,
pub iterations: usize,
pub input_decls: usize,
pub output_decls: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PassId {
JoinPoints,
Specialize,
Reuse,
Dce,
ClosureConvert,
Custom(String),
}
#[derive(Debug, Clone, Default)]
pub struct PassStats {
pub decls_processed: usize,
pub transformations: usize,
pub time_us: u64,
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PipeExtPassPhase {
Early,
Middle,
Late,
Finalize,
}
impl PipeExtPassPhase {
#[allow(dead_code)]
pub fn is_early(&self) -> bool {
matches!(self, Self::Early)
}
#[allow(dead_code)]
pub fn is_middle(&self) -> bool {
matches!(self, Self::Middle)
}
#[allow(dead_code)]
pub fn is_late(&self) -> bool {
matches!(self, Self::Late)
}
#[allow(dead_code)]
pub fn is_finalize(&self) -> bool {
matches!(self, Self::Finalize)
}
#[allow(dead_code)]
pub fn order(&self) -> u32 {
match self {
Self::Early => 0,
Self::Middle => 1,
Self::Late => 2,
Self::Finalize => 3,
}
}
#[allow(dead_code)]
pub fn from_order(n: u32) -> Option<Self> {
match n {
0 => Some(Self::Early),
1 => Some(Self::Middle),
2 => Some(Self::Late),
3 => Some(Self::Finalize),
_ => None,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeExtDepGraph {
pub(crate) n: usize,
pub(crate) adj: Vec<Vec<usize>>,
pub(crate) rev: Vec<Vec<usize>>,
pub(crate) edge_count: usize,
}
impl PipeExtDepGraph {
#[allow(dead_code)]
pub fn new(n: usize) -> Self {
Self {
n,
adj: vec![Vec::new(); n],
rev: vec![Vec::new(); n],
edge_count: 0,
}
}
#[allow(dead_code)]
pub fn add_edge(&mut self, from: usize, to: usize) {
if from < self.n && to < self.n {
if !self.adj[from].contains(&to) {
self.adj[from].push(to);
self.rev[to].push(from);
self.edge_count += 1;
}
}
}
#[allow(dead_code)]
pub fn succs(&self, n: usize) -> &[usize] {
self.adj.get(n).map(|v| v.as_slice()).unwrap_or(&[])
}
#[allow(dead_code)]
pub fn preds(&self, n: usize) -> &[usize] {
self.rev.get(n).map(|v| v.as_slice()).unwrap_or(&[])
}
#[allow(dead_code)]
pub fn topo_sort(&self) -> Option<Vec<usize>> {
let mut deg: Vec<usize> = (0..self.n).map(|i| self.rev[i].len()).collect();
let mut q: std::collections::VecDeque<usize> =
(0..self.n).filter(|&i| deg[i] == 0).collect();
let mut out = Vec::with_capacity(self.n);
while let Some(u) = q.pop_front() {
out.push(u);
for &v in &self.adj[u] {
deg[v] -= 1;
if deg[v] == 0 {
q.push_back(v);
}
}
}
if out.len() == self.n {
Some(out)
} else {
None
}
}
#[allow(dead_code)]
pub fn has_cycle(&self) -> bool {
self.topo_sort().is_none()
}
#[allow(dead_code)]
pub fn reachable(&self, start: usize) -> Vec<usize> {
let mut vis = vec![false; self.n];
let mut stk = vec![start];
let mut out = Vec::new();
while let Some(u) = stk.pop() {
if u < self.n && !vis[u] {
vis[u] = true;
out.push(u);
for &v in &self.adj[u] {
if !vis[v] {
stk.push(v);
}
}
}
}
out
}
#[allow(dead_code)]
pub fn scc(&self) -> Vec<Vec<usize>> {
let mut visited = vec![false; self.n];
let mut order = Vec::new();
for i in 0..self.n {
if !visited[i] {
let mut stk = vec![(i, 0usize)];
while let Some((u, idx)) = stk.last_mut() {
if !visited[*u] {
visited[*u] = true;
}
if *idx < self.adj[*u].len() {
let v = self.adj[*u][*idx];
*idx += 1;
if !visited[v] {
stk.push((v, 0));
}
} else {
order.push(*u);
stk.pop();
}
}
}
}
let mut comp = vec![usize::MAX; self.n];
let mut components: Vec<Vec<usize>> = Vec::new();
for &start in order.iter().rev() {
if comp[start] == usize::MAX {
let cid = components.len();
let mut component = Vec::new();
let mut stk = vec![start];
while let Some(u) = stk.pop() {
if comp[u] == usize::MAX {
comp[u] = cid;
component.push(u);
for &v in &self.rev[u] {
if comp[v] == usize::MAX {
stk.push(v);
}
}
}
}
components.push(component);
}
}
components
}
#[allow(dead_code)]
pub fn node_count(&self) -> usize {
self.n
}
#[allow(dead_code)]
pub fn edge_count(&self) -> usize {
self.edge_count
}
}
#[derive(Debug, Clone)]
pub struct PipelineConfig {
pub opt_level: OptLevel,
pub target: CodegenTarget,
pub debug: bool,
pub emit_ir: bool,
pub passes: Vec<PassId>,
pub max_iterations: usize,
pub emit_comments: bool,
}
impl PipelineConfig {
pub fn effective_passes(&self) -> Vec<PassId> {
if self.passes.is_empty() {
self.opt_level.default_passes()
} else {
self.passes.clone()
}
}
pub fn effective_max_iterations(&self) -> usize {
if self.max_iterations > 0 {
self.max_iterations
} else {
self.opt_level.max_iterations()
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeX2PassConfig {
pub name: String,
pub phase: PipeX2PassPhase,
pub enabled: bool,
pub max_iterations: usize,
pub debug: u32,
pub timeout_ms: Option<u64>,
}
impl PipeX2PassConfig {
#[allow(dead_code)]
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
phase: PipeX2PassPhase::Middle,
enabled: true,
max_iterations: 100,
debug: 0,
timeout_ms: None,
}
}
#[allow(dead_code)]
pub fn with_phase(mut self, phase: PipeX2PassPhase) -> Self {
self.phase = phase;
self
}
#[allow(dead_code)]
pub fn with_max_iter(mut self, n: usize) -> Self {
self.max_iterations = n;
self
}
#[allow(dead_code)]
pub fn with_debug(mut self, d: u32) -> Self {
self.debug = d;
self
}
#[allow(dead_code)]
pub fn disabled(mut self) -> Self {
self.enabled = false;
self
}
#[allow(dead_code)]
pub fn with_timeout(mut self, ms: u64) -> Self {
self.timeout_ms = Some(ms);
self
}
#[allow(dead_code)]
pub fn is_debug_enabled(&self) -> bool {
self.debug > 0
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct PipeX2PassStats {
pub iterations: usize,
pub changed: bool,
pub nodes_visited: usize,
pub nodes_modified: usize,
pub time_ms: u64,
pub memory_bytes: usize,
pub errors: usize,
}
impl PipeX2PassStats {
#[allow(dead_code)]
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
pub fn visit(&mut self) {
self.nodes_visited += 1;
}
#[allow(dead_code)]
pub fn modify(&mut self) {
self.nodes_modified += 1;
self.changed = true;
}
#[allow(dead_code)]
pub fn iterate(&mut self) {
self.iterations += 1;
}
#[allow(dead_code)]
pub fn error(&mut self) {
self.errors += 1;
}
#[allow(dead_code)]
pub fn efficiency(&self) -> f64 {
if self.nodes_visited == 0 {
0.0
} else {
self.nodes_modified as f64 / self.nodes_visited as f64
}
}
#[allow(dead_code)]
pub fn merge(&mut self, o: &PipeX2PassStats) {
self.iterations += o.iterations;
self.changed |= o.changed;
self.nodes_visited += o.nodes_visited;
self.nodes_modified += o.nodes_modified;
self.time_ms += o.time_ms;
self.memory_bytes = self.memory_bytes.max(o.memory_bytes);
self.errors += o.errors;
}
}
#[allow(dead_code)]
pub struct PipePassRegistry {
pub(crate) configs: Vec<PipePassConfig>,
pub(crate) stats: std::collections::HashMap<String, PipePassStats>,
}
impl PipePassRegistry {
#[allow(dead_code)]
pub fn new() -> Self {
PipePassRegistry {
configs: Vec::new(),
stats: std::collections::HashMap::new(),
}
}
#[allow(dead_code)]
pub fn register(&mut self, config: PipePassConfig) {
self.stats
.insert(config.pass_name.clone(), PipePassStats::new());
self.configs.push(config);
}
#[allow(dead_code)]
pub fn enabled_passes(&self) -> Vec<&PipePassConfig> {
self.configs.iter().filter(|c| c.enabled).collect()
}
#[allow(dead_code)]
pub fn get_stats(&self, name: &str) -> Option<&PipePassStats> {
self.stats.get(name)
}
#[allow(dead_code)]
pub fn total_passes(&self) -> usize {
self.configs.len()
}
#[allow(dead_code)]
pub fn enabled_count(&self) -> usize {
self.enabled_passes().len()
}
#[allow(dead_code)]
pub fn update_stats(&mut self, name: &str, changes: u64, time_ms: u64, iter: u32) {
if let Some(stats) = self.stats.get_mut(name) {
stats.record_run(changes, time_ms, iter);
}
}
}
#[allow(dead_code)]
#[derive(Debug, Default)]
pub struct PipeX2PassRegistry {
pub(crate) configs: Vec<PipeX2PassConfig>,
pub(crate) stats: Vec<PipeX2PassStats>,
}
impl PipeX2PassRegistry {
#[allow(dead_code)]
pub fn new() -> Self {
Self::default()
}
#[allow(dead_code)]
pub fn register(&mut self, c: PipeX2PassConfig) {
self.stats.push(PipeX2PassStats::new());
self.configs.push(c);
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.configs.len()
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.configs.is_empty()
}
#[allow(dead_code)]
pub fn get(&self, i: usize) -> Option<&PipeX2PassConfig> {
self.configs.get(i)
}
#[allow(dead_code)]
pub fn get_stats(&self, i: usize) -> Option<&PipeX2PassStats> {
self.stats.get(i)
}
#[allow(dead_code)]
pub fn enabled_passes(&self) -> Vec<&PipeX2PassConfig> {
self.configs.iter().filter(|c| c.enabled).collect()
}
#[allow(dead_code)]
pub fn passes_in_phase(&self, ph: &PipeX2PassPhase) -> Vec<&PipeX2PassConfig> {
self.configs
.iter()
.filter(|c| c.enabled && &c.phase == ph)
.collect()
}
#[allow(dead_code)]
pub fn total_nodes_visited(&self) -> usize {
self.stats.iter().map(|s| s.nodes_visited).sum()
}
#[allow(dead_code)]
pub fn any_changed(&self) -> bool {
self.stats.iter().any(|s| s.changed)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeX2Worklist {
pub(crate) items: std::collections::VecDeque<usize>,
pub(crate) present: Vec<bool>,
}
impl PipeX2Worklist {
#[allow(dead_code)]
pub fn new(capacity: usize) -> Self {
Self {
items: std::collections::VecDeque::new(),
present: vec![false; capacity],
}
}
#[allow(dead_code)]
pub fn push(&mut self, id: usize) {
if id < self.present.len() && !self.present[id] {
self.present[id] = true;
self.items.push_back(id);
}
}
#[allow(dead_code)]
pub fn push_front(&mut self, id: usize) {
if id < self.present.len() && !self.present[id] {
self.present[id] = true;
self.items.push_front(id);
}
}
#[allow(dead_code)]
pub fn pop(&mut self) -> Option<usize> {
let id = self.items.pop_front()?;
if id < self.present.len() {
self.present[id] = false;
}
Some(id)
}
#[allow(dead_code)]
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.items.len()
}
#[allow(dead_code)]
pub fn contains(&self, id: usize) -> bool {
id < self.present.len() && self.present[id]
}
#[allow(dead_code)]
pub fn drain_all(&mut self) -> Vec<usize> {
let v: Vec<usize> = self.items.drain(..).collect();
for &id in &v {
if id < self.present.len() {
self.present[id] = false;
}
}
v
}
}