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::impls1::*;
use super::impls2::*;
use super::super::functions::*;
use super::impls1::*;
use super::impls2::*;
use std::collections::{HashMap, HashSet, VecDeque};
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeDominatorTree {
pub idom: Vec<Option<u32>>,
pub dom_children: Vec<Vec<u32>>,
pub dom_depth: Vec<u32>,
}
impl PipeDominatorTree {
#[allow(dead_code)]
pub fn new(size: usize) -> Self {
PipeDominatorTree {
idom: vec![None; size],
dom_children: vec![Vec::new(); size],
dom_depth: vec![0; size],
}
}
#[allow(dead_code)]
pub fn set_idom(&mut self, node: usize, idom: u32) {
self.idom[node] = Some(idom);
}
#[allow(dead_code)]
pub fn dominates(&self, a: usize, b: usize) -> bool {
if a == b {
return true;
}
let mut cur = b;
loop {
match self.idom[cur] {
Some(parent) if parent as usize == a => return true,
Some(parent) if parent as usize == cur => return false,
Some(parent) => cur = parent as usize,
None => return false,
}
}
}
#[allow(dead_code)]
pub fn depth(&self, node: usize) -> u32 {
self.dom_depth.get(node).copied().unwrap_or(0)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct PipeX2Liveness {
pub live_in: Vec<Vec<usize>>,
pub live_out: Vec<Vec<usize>>,
pub defs: Vec<Vec<usize>>,
pub uses: Vec<Vec<usize>>,
}
impl PipeX2Liveness {
#[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, Clone, Default)]
pub struct PipeX2ConstFolder {
pub(crate) folds: usize,
pub(crate) failures: usize,
pub(crate) enabled: bool,
}
impl PipeX2ConstFolder {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
folds: 0,
failures: 0,
enabled: true,
}
}
#[allow(dead_code)]
pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
self.folds += 1;
a.checked_add(b)
}
#[allow(dead_code)]
pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
self.folds += 1;
a.checked_sub(b)
}
#[allow(dead_code)]
pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
self.folds += 1;
a.checked_mul(b)
}
#[allow(dead_code)]
pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
if b == 0 {
self.failures += 1;
None
} else {
self.folds += 1;
a.checked_div(b)
}
}
#[allow(dead_code)]
pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
if b == 0 {
self.failures += 1;
None
} else {
self.folds += 1;
a.checked_rem(b)
}
}
#[allow(dead_code)]
pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
self.folds += 1;
a.checked_neg()
}
#[allow(dead_code)]
pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
if s >= 64 {
self.failures += 1;
None
} else {
self.folds += 1;
a.checked_shl(s)
}
}
#[allow(dead_code)]
pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
if s >= 64 {
self.failures += 1;
None
} else {
self.folds += 1;
a.checked_shr(s)
}
}
#[allow(dead_code)]
pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
self.folds += 1;
a & b
}
#[allow(dead_code)]
pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
self.folds += 1;
a | b
}
#[allow(dead_code)]
pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
self.folds += 1;
a ^ b
}
#[allow(dead_code)]
pub fn not_i64(&mut self, a: i64) -> i64 {
self.folds += 1;
!a
}
#[allow(dead_code)]
pub fn fold_count(&self) -> usize {
self.folds
}
#[allow(dead_code)]
pub fn failure_count(&self) -> usize {
self.failures
}
#[allow(dead_code)]
pub fn enable(&mut self) {
self.enabled = true;
}
#[allow(dead_code)]
pub fn disable(&mut self) {
self.enabled = false;
}
#[allow(dead_code)]
pub fn is_enabled(&self) -> bool {
self.enabled
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeX2DepGraph {
pub(crate) n: usize,
pub(crate) adj: Vec<Vec<usize>>,
pub(crate) rev: Vec<Vec<usize>>,
pub(crate) edge_count: usize,
}
impl PipeX2DepGraph {
#[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
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeAnalysisCache {
pub(crate) entries: std::collections::HashMap<String, PipeCacheEntry>,
pub(crate) max_size: usize,
pub(crate) hits: u64,
pub(crate) misses: u64,
}
impl PipeAnalysisCache {
#[allow(dead_code)]
pub fn new(max_size: usize) -> Self {
PipeAnalysisCache {
entries: std::collections::HashMap::new(),
max_size,
hits: 0,
misses: 0,
}
}
#[allow(dead_code)]
pub fn get(&mut self, key: &str) -> Option<&PipeCacheEntry> {
if self.entries.contains_key(key) {
self.hits += 1;
self.entries.get(key)
} else {
self.misses += 1;
None
}
}
#[allow(dead_code)]
pub fn insert(&mut self, key: String, data: Vec<u8>) {
if self.entries.len() >= self.max_size {
if let Some(oldest) = self.entries.keys().next().cloned() {
self.entries.remove(&oldest);
}
}
self.entries.insert(
key.clone(),
PipeCacheEntry {
key,
data,
timestamp: 0,
valid: true,
},
);
}
#[allow(dead_code)]
pub fn invalidate(&mut self, key: &str) {
if let Some(entry) = self.entries.get_mut(key) {
entry.valid = false;
}
}
#[allow(dead_code)]
pub fn clear(&mut self) {
self.entries.clear();
}
#[allow(dead_code)]
pub fn hit_rate(&self) -> f64 {
let total = self.hits + self.misses;
if total == 0 {
return 0.0;
}
self.hits as f64 / total as f64
}
#[allow(dead_code)]
pub fn size(&self) -> usize {
self.entries.len()
}
}
pub struct PipelineBuilder {
pub(crate) config: PipelineConfig,
}
impl PipelineBuilder {
pub fn new() -> Self {
Self {
config: PipelineConfig::default(),
}
}
pub fn opt_level(mut self, level: OptLevel) -> Self {
self.config.opt_level = level;
self
}
pub fn target(mut self, target: CodegenTarget) -> Self {
self.config.target = target;
self
}
pub fn debug(mut self) -> Self {
self.config.debug = true;
self
}
pub fn emit_ir(mut self) -> Self {
self.config.emit_ir = true;
self
}
pub fn with_passes(mut self, passes: Vec<PassId>) -> Self {
self.config.passes = passes;
self
}
pub fn max_iterations(mut self, n: usize) -> Self {
self.config.max_iterations = n;
self
}
pub fn build(self) -> PipelineConfig {
self.config
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct PipeExtConstFolder {
pub(crate) folds: usize,
pub(crate) failures: usize,
pub(crate) enabled: bool,
}
impl PipeExtConstFolder {
#[allow(dead_code)]
pub fn new() -> Self {
Self {
folds: 0,
failures: 0,
enabled: true,
}
}
#[allow(dead_code)]
pub fn add_i64(&mut self, a: i64, b: i64) -> Option<i64> {
self.folds += 1;
a.checked_add(b)
}
#[allow(dead_code)]
pub fn sub_i64(&mut self, a: i64, b: i64) -> Option<i64> {
self.folds += 1;
a.checked_sub(b)
}
#[allow(dead_code)]
pub fn mul_i64(&mut self, a: i64, b: i64) -> Option<i64> {
self.folds += 1;
a.checked_mul(b)
}
#[allow(dead_code)]
pub fn div_i64(&mut self, a: i64, b: i64) -> Option<i64> {
if b == 0 {
self.failures += 1;
None
} else {
self.folds += 1;
a.checked_div(b)
}
}
#[allow(dead_code)]
pub fn rem_i64(&mut self, a: i64, b: i64) -> Option<i64> {
if b == 0 {
self.failures += 1;
None
} else {
self.folds += 1;
a.checked_rem(b)
}
}
#[allow(dead_code)]
pub fn neg_i64(&mut self, a: i64) -> Option<i64> {
self.folds += 1;
a.checked_neg()
}
#[allow(dead_code)]
pub fn shl_i64(&mut self, a: i64, s: u32) -> Option<i64> {
if s >= 64 {
self.failures += 1;
None
} else {
self.folds += 1;
a.checked_shl(s)
}
}
#[allow(dead_code)]
pub fn shr_i64(&mut self, a: i64, s: u32) -> Option<i64> {
if s >= 64 {
self.failures += 1;
None
} else {
self.folds += 1;
a.checked_shr(s)
}
}
#[allow(dead_code)]
pub fn and_i64(&mut self, a: i64, b: i64) -> i64 {
self.folds += 1;
a & b
}
#[allow(dead_code)]
pub fn or_i64(&mut self, a: i64, b: i64) -> i64 {
self.folds += 1;
a | b
}
#[allow(dead_code)]
pub fn xor_i64(&mut self, a: i64, b: i64) -> i64 {
self.folds += 1;
a ^ b
}
#[allow(dead_code)]
pub fn not_i64(&mut self, a: i64) -> i64 {
self.folds += 1;
!a
}
#[allow(dead_code)]
pub fn fold_count(&self) -> usize {
self.folds
}
#[allow(dead_code)]
pub fn failure_count(&self) -> usize {
self.failures
}
#[allow(dead_code)]
pub fn enable(&mut self) {
self.enabled = true;
}
#[allow(dead_code)]
pub fn disable(&mut self) {
self.enabled = false;
}
#[allow(dead_code)]
pub fn is_enabled(&self) -> bool {
self.enabled
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipePassConfig {
pub phase: PipePassPhase,
pub enabled: bool,
pub max_iterations: u32,
pub debug_output: bool,
pub pass_name: String,
}
impl PipePassConfig {
#[allow(dead_code)]
pub fn new(name: impl Into<String>, phase: PipePassPhase) -> Self {
PipePassConfig {
phase,
enabled: true,
max_iterations: 10,
debug_output: false,
pass_name: name.into(),
}
}
#[allow(dead_code)]
pub fn disabled(mut self) -> Self {
self.enabled = false;
self
}
#[allow(dead_code)]
pub fn with_debug(mut self) -> Self {
self.debug_output = true;
self
}
#[allow(dead_code)]
pub fn max_iter(mut self, n: u32) -> Self {
self.max_iterations = n;
self
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeCacheEntry {
pub key: String,
pub data: Vec<u8>,
pub timestamp: u64,
pub valid: bool,
}
#[derive(Debug, Clone)]
pub struct PassResult {
pub module: LcnfModule,
pub stats: PassStats,
pub changed: bool,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeExtDomTree {
pub(crate) idom: Vec<Option<usize>>,
pub(crate) children: Vec<Vec<usize>>,
pub(crate) depth: Vec<usize>,
}
impl PipeExtDomTree {
#[allow(dead_code)]
pub fn new(n: usize) -> Self {
Self {
idom: vec![None; n],
children: vec![Vec::new(); n],
depth: vec![0; n],
}
}
#[allow(dead_code)]
pub fn set_idom(&mut self, node: usize, dom: usize) {
if node < self.idom.len() {
self.idom[node] = Some(dom);
if dom < self.children.len() {
self.children[dom].push(node);
}
self.depth[node] = if dom < self.depth.len() {
self.depth[dom] + 1
} else {
1
};
}
}
#[allow(dead_code)]
pub fn dominates(&self, a: usize, mut b: usize) -> bool {
if a == b {
return true;
}
let n = self.idom.len();
for _ in 0..n {
match self.idom.get(b).copied().flatten() {
None => return false,
Some(p) if p == a => return true,
Some(p) if p == b => return false,
Some(p) => b = p,
}
}
false
}
#[allow(dead_code)]
pub fn children_of(&self, n: usize) -> &[usize] {
self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
}
#[allow(dead_code)]
pub fn depth_of(&self, n: usize) -> usize {
self.depth.get(n).copied().unwrap_or(0)
}
#[allow(dead_code)]
pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
let n = self.idom.len();
for _ in 0..(2 * n) {
if a == b {
return a;
}
if self.depth_of(a) > self.depth_of(b) {
a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
} else {
b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
}
}
0
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PipeX2DomTree {
pub(crate) idom: Vec<Option<usize>>,
pub(crate) children: Vec<Vec<usize>>,
pub(crate) depth: Vec<usize>,
}
impl PipeX2DomTree {
#[allow(dead_code)]
pub fn new(n: usize) -> Self {
Self {
idom: vec![None; n],
children: vec![Vec::new(); n],
depth: vec![0; n],
}
}
#[allow(dead_code)]
pub fn set_idom(&mut self, node: usize, dom: usize) {
if node < self.idom.len() {
self.idom[node] = Some(dom);
if dom < self.children.len() {
self.children[dom].push(node);
}
self.depth[node] = if dom < self.depth.len() {
self.depth[dom] + 1
} else {
1
};
}
}
#[allow(dead_code)]
pub fn dominates(&self, a: usize, mut b: usize) -> bool {
if a == b {
return true;
}
let n = self.idom.len();
for _ in 0..n {
match self.idom.get(b).copied().flatten() {
None => return false,
Some(p) if p == a => return true,
Some(p) if p == b => return false,
Some(p) => b = p,
}
}
false
}
#[allow(dead_code)]
pub fn children_of(&self, n: usize) -> &[usize] {
self.children.get(n).map(|v| v.as_slice()).unwrap_or(&[])
}
#[allow(dead_code)]
pub fn depth_of(&self, n: usize) -> usize {
self.depth.get(n).copied().unwrap_or(0)
}
#[allow(dead_code)]
pub fn lca(&self, mut a: usize, mut b: usize) -> usize {
let n = self.idom.len();
for _ in 0..(2 * n) {
if a == b {
return a;
}
if self.depth_of(a) > self.depth_of(b) {
a = self.idom.get(a).and_then(|x| *x).unwrap_or(a);
} else {
b = self.idom.get(b).and_then(|x| *x).unwrap_or(b);
}
}
0
}
}