use llvm_native_core::analysis::{CallGraph, DominatorTree, LoopInfo};
use llvm_native_core::module::Module;
use llvm_native_core::value::ValueRef;
use std::any::{Any, TypeId};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PassResult {
Unchanged,
Changed,
Error,
}
#[derive(Debug, Clone, Default)]
pub struct PassStats {
pub changes: usize,
pub functions_processed: usize,
pub blocks_processed: usize,
pub instructions_eliminated: usize,
}
pub trait Pass: Any {
fn name(&self) -> &'static str;
fn pass_id(&self) -> TypeId {
TypeId::of::<Self>()
}
}
pub trait ModulePass: Pass {
fn run_on_module(&mut self, module: &mut Module, am: &mut AnalysisManager) -> PassResult;
}
pub trait FunctionPass: Pass {
fn run_on_function(&mut self, func: &ValueRef, am: &AnalysisManager) -> PassResult;
}
pub struct AnalysisManager {
cache: HashMap<TypeId, Box<dyn Any>>,
pub stats: HashMap<String, PassStats>,
preserved: HashSet<String>,
named_results: HashMap<String, Box<dyn AnalysisResult>>,
}
impl AnalysisManager {
pub fn new() -> Self {
Self {
cache: HashMap::new(),
stats: HashMap::new(),
preserved: HashSet::new(),
named_results: HashMap::new(),
}
}
pub fn get_analysis<T: Any>(&mut self) -> Option<&T> {
let type_id = TypeId::of::<T>();
if self.cache.contains_key(&type_id) {
return self.cache.get(&type_id).and_then(|b| b.downcast_ref::<T>());
}
None
}
pub fn set_analysis<T: Any>(&mut self, result: T) {
let type_id = TypeId::of::<T>();
self.cache.insert(type_id, Box::new(result));
}
pub fn invalidate<T: Any>(&mut self) {
let type_id = TypeId::of::<T>();
self.cache.remove(&type_id);
}
pub fn invalidate_all(&mut self) {
self.cache.clear();
self.named_results.clear();
}
pub fn record_stats(&mut self, pass_name: &str, stats: PassStats) {
self.stats.insert(pass_name.to_string(), stats);
}
pub fn print_stats(&self) {
println!("=== Pass Statistics ===");
for (name, stats) in &self.stats {
println!(
" {}: {} changes, {} functions, {} blocks, {} eliminated",
name,
stats.changes,
stats.functions_processed,
stats.blocks_processed,
stats.instructions_eliminated
);
}
}
pub fn get_named_analysis(&self, key: &str) -> Option<&dyn AnalysisResult> {
self.named_results.get(key).map(|b| b.as_ref())
}
pub fn get_analysis_typed<T: 'static>(&self, key: &str) -> Option<&T> {
self.named_results.get(key).and_then(|b| {
let any_ref: &dyn std::any::Any = b.as_ref();
any_ref.downcast_ref::<T>()
})
}
pub fn set_named_analysis<T: AnalysisResult + 'static>(&mut self, key: &str, result: T) {
self.named_results.insert(key.to_string(), Box::new(result));
}
pub fn invalidate_by_pass(&mut self, pass_name: &str) {
self.named_results
.retain(|_key, result| !result.invalidate_on(pass_name));
}
pub fn mark_preserved(&mut self, analysis_name: &str) {
self.preserved.insert(analysis_name.to_string());
}
pub fn is_preserved(&self, analysis_name: &str) -> bool {
self.preserved.contains(analysis_name)
}
pub fn clear_preserved(&mut self) {
self.preserved.clear();
}
pub fn clear(&mut self) {
self.cache.clear();
self.named_results.clear();
self.preserved.clear();
}
}
impl Default for AnalysisManager {
fn default() -> Self {
Self::new()
}
}
pub struct PassManager {
passes: Vec<Box<dyn ModulePassWrapper>>,
pub analysis_manager: AnalysisManager,
pub debug: bool,
pub verify_each: bool,
pub time_passes: bool,
pub pass_timings: Vec<(String, u64)>,
}
trait ModulePassWrapper {
fn run(&mut self, module: &mut Module, am: &mut AnalysisManager) -> PassResult;
fn name(&self) -> &'static str;
}
struct FunctionPassAdapter {
pass: Box<dyn FunctionPass>,
}
impl ModulePassWrapper for FunctionPassAdapter {
fn run(&mut self, module: &mut Module, am: &mut AnalysisManager) -> PassResult {
let mut overall = PassResult::Unchanged;
let mut stats = PassStats::default();
for func in &module.functions {
let result = self.pass.run_on_function(func, am);
if result == PassResult::Changed {
overall = PassResult::Changed;
}
stats.functions_processed += 1;
stats.changes += match result {
PassResult::Changed => 1,
_ => 0,
};
}
am.record_stats(self.pass.name(), stats);
overall
}
fn name(&self) -> &'static str {
self.pass.name()
}
}
struct ModulePassAdapter {
pass: Box<dyn ModulePass>,
}
impl ModulePassWrapper for ModulePassAdapter {
fn run(&mut self, module: &mut Module, am: &mut AnalysisManager) -> PassResult {
let result = self.pass.run_on_module(module, am);
let stats = PassStats {
changes: if result == PassResult::Changed { 1 } else { 0 },
..Default::default()
};
am.record_stats(self.pass.name(), stats);
result
}
fn name(&self) -> &'static str {
self.pass.name()
}
}
impl PassManager {
pub fn new() -> Self {
Self {
passes: Vec::new(),
analysis_manager: AnalysisManager::new(),
debug: false,
verify_each: false,
time_passes: false,
pass_timings: Vec::new(),
}
}
pub fn add_function_pass(&mut self, pass: Box<dyn FunctionPass>) {
self.passes.push(Box::new(FunctionPassAdapter { pass }));
}
pub fn add_module_pass(&mut self, pass: Box<dyn ModulePass>) {
self.passes.push(Box::new(ModulePassAdapter { pass }));
}
pub fn run(&mut self, module: &mut Module) -> bool {
let mut changed = false;
for pass in &mut self.passes {
let name = pass.name();
let start = if self.time_passes {
Some(std::time::Instant::now())
} else {
None
};
if self.debug {
eprintln!("PassManager: running pass '{}'", name);
}
let result = pass.run(module, &mut self.analysis_manager);
match result {
PassResult::Changed => {
changed = true;
self.analysis_manager.invalidate_all();
}
PassResult::Error => {
eprintln!("PassManager: pass '{}' returned Error", name);
}
PassResult::Unchanged => {}
}
if let Some(start) = start {
let elapsed = start.elapsed().as_micros() as u64;
self.pass_timings.push((name.to_string(), elapsed));
}
}
changed
}
pub fn print_stats(&self) {
self.analysis_manager.print_stats();
}
pub fn print_pass_timing(&self) {
if self.pass_timings.is_empty() {
println!("(no pass timing data)");
return;
}
println!("=== Pass Timing ===");
let mut total: u64 = 0;
for (name, us) in &self.pass_timings {
println!(" {}: {} us", name, us);
total += us;
}
println!(" Total: {} us", total);
}
pub fn add_unified_pass(&mut self, _pass: Box<dyn TransformPass>) {
}
pub fn run_pipeline(&mut self, module: &mut Module, pipeline: &[String]) -> bool {
let mut changed = false;
for pass_name in pipeline {
if self.debug {
eprintln!("PassManager: running pipeline pass '{}'", pass_name);
}
let mut found = false;
for pass in &mut self.passes {
if pass.name() == pass_name.as_str() {
let result = pass.run(module, &mut self.analysis_manager);
if result == PassResult::Changed {
changed = true;
self.analysis_manager.invalidate_all();
}
found = true;
break;
}
}
if !found && self.debug {
eprintln!("PassManager: pass '{}' not found in pipeline", pass_name);
}
}
changed
}
pub fn register_defaults(&mut self) {
}
pub fn analysis_manager(&self) -> &AnalysisManager {
&self.analysis_manager
}
pub fn analysis_manager_mut(&mut self) -> &mut AnalysisManager {
&mut self.analysis_manager
}
}
impl Default for PassManager {
fn default() -> Self {
Self::new()
}
}
pub fn get_dominator_tree(func: &ValueRef, am: &mut AnalysisManager) -> DominatorTree {
let _key = (func.borrow().vid, TypeId::of::<DominatorTree>());
if let Some(dt) = am.get_analysis::<DominatorTree>() {
return dt.clone();
}
let dt = DominatorTree::compute(func);
am.set_analysis(dt.clone());
dt
}
pub fn get_loop_info(func: &ValueRef, dt: &DominatorTree, am: &mut AnalysisManager) -> LoopInfo {
if let Some(li) = am.get_analysis::<LoopInfo>() {
return li.clone();
}
let li = LoopInfo::compute(func, dt);
am.set_analysis(li.clone());
li
}
pub fn get_call_graph(module: &Module, am: &mut AnalysisManager) -> CallGraph {
if let Some(cg) = am.get_analysis::<CallGraph>() {
return cg.clone();
}
let cg = CallGraph::compute(module);
am.set_analysis(cg.clone());
cg
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum OptimizationLevel {
O0,
O1,
O2,
O3,
Os,
Oz,
}
impl OptimizationLevel {
pub fn as_str(&self) -> &'static str {
match self {
OptimizationLevel::O0 => "O0",
OptimizationLevel::O1 => "O1",
OptimizationLevel::O2 => "O2",
OptimizationLevel::O3 => "O3",
OptimizationLevel::Os => "Os",
OptimizationLevel::Oz => "Oz",
}
}
pub fn is_optimizing(&self) -> bool {
!matches!(self, OptimizationLevel::O0)
}
pub fn is_aggressive(&self) -> bool {
matches!(
self,
OptimizationLevel::O2 | OptimizationLevel::O3 | OptimizationLevel::Oz
)
}
}
pub trait AnalysisResult: std::fmt::Debug + std::any::Any {
fn invalidate_on(&self, pass_name: &str) -> bool;
fn name(&self) -> &'static str;
fn clone_box(&self) -> Box<dyn AnalysisResult>;
}
impl Clone for Box<dyn AnalysisResult> {
fn clone(&self) -> Self {
self.as_ref().clone_box()
}
}
#[derive(Debug, Clone)]
pub struct TypedAnalysis<T: Clone + std::fmt::Debug + 'static> {
pub value: T,
pub analysis_name: &'static str,
pub invalidators: Vec<String>,
}
impl<T: Clone + std::fmt::Debug + 'static> TypedAnalysis<T> {
pub fn new(value: T, name: &'static str) -> Self {
Self {
value,
analysis_name: name,
invalidators: Vec::new(),
}
}
pub fn add_invalidator(mut self, pass_name: &str) -> Self {
self.invalidators.push(pass_name.to_string());
self
}
}
impl<T: Clone + std::fmt::Debug + 'static> AnalysisResult for TypedAnalysis<T> {
fn invalidate_on(&self, pass_name: &str) -> bool {
self.invalidators.iter().any(|name| name == pass_name)
}
fn name(&self) -> &'static str {
self.analysis_name
}
fn clone_box(&self) -> Box<dyn AnalysisResult> {
Box::new(self.clone())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PassRequirement {
pub analysis_name: String,
pub required: bool,
}
impl PassRequirement {
pub fn required(name: &str) -> Self {
Self {
analysis_name: name.to_string(),
required: true,
}
}
pub fn optional(name: &str) -> Self {
Self {
analysis_name: name.to_string(),
required: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PassTiming {
Early,
Standard,
Late,
}
#[derive(Debug, Clone)]
pub struct PassInfo {
pub name: String,
pub description: String,
pub timing: PassTiming,
pub is_analysis: bool,
pub is_transform: bool,
pub requires: Vec<String>,
pub invalidates: Vec<String>,
pub preserves: Vec<String>,
pub opt_levels: Vec<OptimizationLevel>,
}
impl PassInfo {
pub fn new(name: &str, description: &str) -> Self {
Self {
name: name.to_string(),
description: description.to_string(),
timing: PassTiming::Standard,
is_analysis: false,
is_transform: false,
requires: Vec::new(),
invalidates: Vec::new(),
preserves: Vec::new(),
opt_levels: vec![
OptimizationLevel::O1,
OptimizationLevel::O2,
OptimizationLevel::O3,
OptimizationLevel::Os,
OptimizationLevel::Oz,
],
}
}
pub fn set_analysis(mut self) -> Self {
self.is_analysis = true;
self
}
pub fn set_transform(mut self) -> Self {
self.is_transform = true;
self
}
pub fn add_required(mut self, analysis: &str) -> Self {
self.requires.push(analysis.to_string());
self
}
pub fn add_invalidated(mut self, analysis: &str) -> Self {
self.invalidates.push(analysis.to_string());
self
}
pub fn add_preserved(mut self, analysis: &str) -> Self {
self.preserves.push(analysis.to_string());
self
}
pub fn with_timing(mut self, timing: PassTiming) -> Self {
self.timing = timing;
self
}
pub fn with_opt_levels(mut self, levels: Vec<OptimizationLevel>) -> Self {
self.opt_levels = levels;
self
}
}
#[derive(Debug, Clone)]
pub struct PassRegistry {
pub passes: HashMap<String, PassInfo>,
pub order: Vec<String>,
}
impl PassRegistry {
pub fn new() -> Self {
Self {
passes: HashMap::new(),
order: Vec::new(),
}
}
pub fn register(&mut self, info: PassInfo) {
let name = info.name.clone();
if !self.passes.contains_key(&name) {
self.order.push(name.clone());
}
self.passes.insert(name, info);
}
pub fn register_default_passes(&mut self) {
self.register(
PassInfo::new("domtree", "Dominator Tree Construction")
.set_analysis()
.with_timing(PassTiming::Early),
);
self.register(
PassInfo::new("loops", "Natural Loop Info")
.set_analysis()
.with_timing(PassTiming::Early)
.add_required("domtree"),
);
self.register(
PassInfo::new("callgraph", "Call Graph Construction")
.set_analysis()
.with_timing(PassTiming::Early),
);
self.register(
PassInfo::new("scalar-evolution", "Scalar Evolution Analysis")
.set_analysis()
.with_timing(PassTiming::Standard)
.add_required("loops"),
);
self.register(
PassInfo::new("dce", "Dead Code Elimination")
.set_transform()
.with_timing(PassTiming::Early)
.add_preserved("domtree"),
);
self.register(
PassInfo::new("instcombine", "Instruction Combining")
.set_transform()
.with_timing(PassTiming::Standard)
.add_required("domtree")
.add_invalidated("loops"),
);
self.register(
PassInfo::new("simplifycfg", "Simplify the CFG")
.set_transform()
.with_timing(PassTiming::Early)
.add_invalidated("domtree")
.add_invalidated("loops"),
);
self.register(
PassInfo::new("mem2reg", "Promote Memory to Register")
.set_transform()
.with_timing(PassTiming::Standard)
.add_invalidated("domtree")
.add_invalidated("loops"),
);
self.register(
PassInfo::new("gvn", "Global Value Numbering")
.set_transform()
.with_timing(PassTiming::Standard)
.add_required("domtree")
.add_invalidated("loops"),
);
self.register(
PassInfo::new("inline", "Function Inlining")
.set_transform()
.with_timing(PassTiming::Standard)
.add_required("callgraph")
.add_invalidated("domtree")
.add_invalidated("loops"),
);
self.register(
PassInfo::new("licm", "Loop Invariant Code Motion")
.set_transform()
.with_timing(PassTiming::Standard)
.add_required("loops"),
);
self.register(
PassInfo::new("loop-unroll", "Loop Unrolling")
.set_transform()
.with_timing(PassTiming::Late)
.add_required("loops")
.add_required("scalar-evolution"),
);
self.register(
PassInfo::new("loop-rotate", "Loop Rotation")
.set_transform()
.with_timing(PassTiming::Standard)
.add_required("loops"),
);
self.register(
PassInfo::new("tailcallelim", "Tail Call Elimination")
.set_transform()
.with_timing(PassTiming::Late)
.add_required("callgraph"),
);
self.register(
PassInfo::new("reassociate", "Reassociation")
.set_transform()
.with_timing(PassTiming::Standard),
);
self.register(
PassInfo::new("early-cse", "Early CSE")
.set_transform()
.with_timing(PassTiming::Early)
.add_preserved("domtree"),
);
}
pub fn get(&self, name: &str) -> Option<&PassInfo> {
self.passes.get(name)
}
pub fn has(&self, name: &str) -> bool {
self.passes.contains_key(name)
}
pub fn get_pipeline(&self, level: OptimizationLevel) -> Vec<String> {
let mut pipeline: Vec<&PassInfo> = self
.passes
.values()
.filter(|info| info.is_transform && info.opt_levels.contains(&level))
.collect();
pipeline.sort_by_key(|info| match info.timing {
PassTiming::Early => 0,
PassTiming::Standard => 1,
PassTiming::Late => 2,
});
pipeline.iter().map(|info| info.name.clone()).collect()
}
pub fn collect_requirements(&self, pass_names: &[String]) -> Vec<String> {
let mut reqs = Vec::new();
for name in pass_names {
if let Some(info) = self.passes.get(name) {
for req in &info.requires {
if !reqs.contains(req) {
reqs.push(req.clone());
}
}
}
}
reqs
}
pub fn validate_pipeline(&self, pipeline: &[String]) -> Vec<String> {
let mut missing = Vec::new();
let mut available: HashSet<String> = self
.passes
.values()
.filter(|info| info.is_analysis)
.map(|info| info.name.clone())
.collect();
for name in pipeline {
if self.passes.contains_key(name) {
available.insert(name.clone());
}
}
for name in pipeline {
if let Some(info) = self.passes.get(name) {
for req in &info.requires {
if !available.contains(req) && !missing.contains(req) {
missing.push(req.clone());
}
}
}
}
missing
}
pub fn iter(&self) -> impl Iterator<Item = &PassInfo> {
self.passes.values()
}
pub fn len(&self) -> usize {
self.passes.len()
}
pub fn is_empty(&self) -> bool {
self.passes.is_empty()
}
pub fn remove(&mut self, name: &str) -> Option<PassInfo> {
self.order.retain(|n| n != name);
self.passes.remove(name)
}
pub fn clear(&mut self) {
self.passes.clear();
self.order.clear();
}
}
impl Default for PassRegistry {
fn default() -> Self {
Self::new()
}
}
pub trait TransformPass: Send + Sync {
fn name(&self) -> &str;
fn run_on_module(&mut self, _module: &mut Module, _am: &mut AnalysisManager) -> bool {
false
}
fn run_on_function(&mut self, _func: &ValueRef, _am: &mut AnalysisManager) -> bool {
false
}
fn get_requirements(&self) -> Vec<PassRequirement> {
Vec::new()
}
fn get_preserved_analyses(&self) -> Vec<String> {
Vec::new()
}
fn get_invalidated_analyses(&self) -> Vec<String> {
Vec::new()
}
fn is_module_pass(&self) -> bool {
false
}
fn is_function_pass(&self) -> bool {
true
}
}
#[derive(Default)]
pub struct PassInstrumentationCallbacks {
before_pass_module: Vec<Box<dyn Fn(&str, &Module) + Send + Sync>>,
after_pass_module: Vec<Box<dyn Fn(&str, &Module, bool) + Send + Sync>>,
before_pass_function: Vec<Box<dyn Fn(&str, &ValueRef) + Send + Sync>>,
after_pass_function: Vec<Box<dyn Fn(&str, &ValueRef, bool) + Send + Sync>>,
pass_skipped: Vec<Box<dyn Fn(&str) + Send + Sync>>,
before_analysis: Vec<Box<dyn Fn(&str, &str) + Send + Sync>>,
after_analysis: Vec<Box<dyn Fn(&str, &str) + Send + Sync>>,
}
impl PassInstrumentationCallbacks {
pub fn new() -> Self {
Self::default()
}
pub fn register_before_pass_module<F>(&mut self, callback: F)
where
F: Fn(&str, &Module) + Send + Sync + 'static,
{
self.before_pass_module.push(Box::new(callback));
}
pub fn register_after_pass_module<F>(&mut self, callback: F)
where
F: Fn(&str, &Module, bool) + Send + Sync + 'static,
{
self.after_pass_module.push(Box::new(callback));
}
pub fn register_before_pass_function<F>(&mut self, callback: F)
where
F: Fn(&str, &ValueRef) + Send + Sync + 'static,
{
self.before_pass_function.push(Box::new(callback));
}
pub fn register_after_pass_function<F>(&mut self, callback: F)
where
F: Fn(&str, &ValueRef, bool) + Send + Sync + 'static,
{
self.after_pass_function.push(Box::new(callback));
}
pub fn register_pass_skipped<F>(&mut self, callback: F)
where
F: Fn(&str) + Send + Sync + 'static,
{
self.pass_skipped.push(Box::new(callback));
}
pub fn register_before_analysis<F>(&mut self, callback: F)
where
F: Fn(&str, &str) + Send + Sync + 'static,
{
self.before_analysis.push(Box::new(callback));
}
pub fn register_after_analysis<F>(&mut self, callback: F)
where
F: Fn(&str, &str) + Send + Sync + 'static,
{
self.after_analysis.push(Box::new(callback));
}
pub fn run_before_pass_module(&self, pass_name: &str, module: &Module) {
for cb in &self.before_pass_module {
cb(pass_name, module);
}
}
pub fn run_after_pass_module(&self, pass_name: &str, module: &Module, changed: bool) {
for cb in &self.after_pass_module {
cb(pass_name, module, changed);
}
}
pub fn run_before_pass_function(&self, pass_name: &str, func: &ValueRef) {
for cb in &self.before_pass_function {
cb(pass_name, func);
}
}
pub fn run_after_pass_function(&self, pass_name: &str, func: &ValueRef, changed: bool) {
for cb in &self.after_pass_function {
cb(pass_name, func, changed);
}
}
pub fn run_pass_skipped(&self, pass_name: &str) {
for cb in &self.pass_skipped {
cb(pass_name);
}
}
pub fn run_before_analysis(&self, pass_name: &str, analysis_name: &str) {
for cb in &self.before_analysis {
cb(pass_name, analysis_name);
}
}
pub fn run_after_analysis(&self, pass_name: &str, analysis_name: &str) {
for cb in &self.after_analysis {
cb(pass_name, analysis_name);
}
}
pub fn clear(&mut self) {
self.before_pass_module.clear();
self.after_pass_module.clear();
self.before_pass_function.clear();
self.after_pass_function.clear();
self.pass_skipped.clear();
self.before_analysis.clear();
self.after_analysis.clear();
}
}
impl std::fmt::Debug for PassInstrumentationCallbacks {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PassInstrumentationCallbacks")
.field("before_pass_module_count", &self.before_pass_module.len())
.field("after_pass_module_count", &self.after_pass_module.len())
.field(
"before_pass_function_count",
&self.before_pass_function.len(),
)
.field("after_pass_function_count", &self.after_pass_function.len())
.field("pass_skipped_count", &self.pass_skipped.len())
.field("before_analysis_count", &self.before_analysis.len())
.field("after_analysis_count", &self.after_analysis.len())
.finish()
}
}
#[derive(Debug, Clone)]
pub struct TimePassesHandler {
pub enabled: bool,
timings: HashMap<String, (u128, usize)>,
current_pass_start: Option<(String, std::time::Instant)>,
pub per_function: bool,
}
impl TimePassesHandler {
pub fn new(enabled: bool) -> Self {
Self {
enabled,
timings: HashMap::new(),
current_pass_start: None,
per_function: false,
}
}
pub fn start_pass(&mut self, pass_name: &str) {
if !self.enabled {
return;
}
self.current_pass_start = Some((pass_name.to_string(), std::time::Instant::now()));
}
pub fn stop_pass(&mut self) {
if !self.enabled {
return;
}
if let Some((name, start)) = self.current_pass_start.take() {
let elapsed = start.elapsed().as_nanos();
let entry = self.timings.entry(name).or_insert((0, 0));
entry.0 += elapsed;
entry.1 += 1;
}
}
pub fn time_for_pass(&self, pass_name: &str) -> Option<u128> {
self.timings.get(pass_name).map(|(t, _)| *t)
}
pub fn count_for_pass(&self, pass_name: &str) -> Option<usize> {
self.timings.get(pass_name).map(|(_, c)| *c)
}
pub fn all_timings(&self) -> &HashMap<String, (u128, usize)> {
&self.timings
}
pub fn report(&self) -> String {
if !self.enabled {
return "TimePassesHandler is disabled.".to_string();
}
let mut entries: Vec<(&String, &(u128, usize))> = self.timings.iter().collect();
entries.sort_by(|a, b| b.1 .0.cmp(&a.1 .0));
let mut report = String::from("=== Pass Timing Report ===\n");
report.push_str(&format!(
"{:<30} {:>12} {:>8}\n",
"Pass Name", "Time (ms)", "Count"
));
report.push_str(&"-".repeat(52));
report.push('\n');
for (name, (total_ns, count)) in &entries {
let ms = *total_ns as f64 / 1_000_000.0;
report.push_str(&format!("{:<30} {:>12.3} {:>8}\n", name, ms, count));
}
report
}
pub fn reset(&mut self) {
self.timings.clear();
self.current_pass_start = None;
}
}
impl Default for TimePassesHandler {
fn default() -> Self {
Self::new(false)
}
}
#[derive(Debug, Clone, Default)]
pub struct PassStatistics {
stats: HashMap<String, StatRecord>,
pub enabled: bool,
}
#[derive(Debug, Clone, Default)]
pub struct StatRecord {
pub runs: usize,
pub changes: usize,
pub functions: usize,
pub insts_eliminated: usize,
pub blocks_modified: usize,
pub total_time_ns: u128,
}
impl PassStatistics {
pub fn new(enabled: bool) -> Self {
Self {
stats: HashMap::new(),
enabled,
}
}
pub fn record_run(&mut self, pass_name: &str, changed: bool) {
if !self.enabled {
return;
}
let entry = self.stats.entry(pass_name.to_string()).or_default();
entry.runs += 1;
if changed {
entry.changes += 1;
}
}
pub fn record_function(&mut self, pass_name: &str) {
if !self.enabled {
return;
}
let entry = self.stats.entry(pass_name.to_string()).or_default();
entry.functions += 1;
}
pub fn record_inst_eliminated(&mut self, pass_name: &str, count: usize) {
if !self.enabled {
return;
}
let entry = self.stats.entry(pass_name.to_string()).or_default();
entry.insts_eliminated += count;
}
pub fn record_block_modified(&mut self, pass_name: &str) {
if !self.enabled {
return;
}
let entry = self.stats.entry(pass_name.to_string()).or_default();
entry.blocks_modified += 1;
}
pub fn set_timing(&mut self, pass_name: &str, time_ns: u128) {
if !self.enabled {
return;
}
let entry = self.stats.entry(pass_name.to_string()).or_default();
entry.total_time_ns = time_ns;
}
pub fn get(&self, pass_name: &str) -> Option<&StatRecord> {
self.stats.get(pass_name)
}
pub fn report(&self) -> String {
if !self.enabled {
return "PassStatistics is disabled.".to_string();
}
let mut entries: Vec<(&String, &StatRecord)> = self.stats.iter().collect();
entries.sort_by(|a, b| b.1.runs.cmp(&a.1.runs));
let mut report = String::from("=== Pass Statistics ===\n");
report.push_str(&format!(
"{:<25} {:>6} {:>7} {:>7} {:>7} {:>7}\n",
"Pass", "Runs", "Changed", "Funcs", "InstElim", "Blocks"
));
report.push_str(&"-".repeat(68));
report.push('\n');
for (name, rec) in &entries {
report.push_str(&format!(
"{:<25} {:>6} {:>7} {:>7} {:>7} {:>7}\n",
name,
rec.runs,
rec.changes,
rec.functions,
rec.insts_eliminated,
rec.blocks_modified
));
}
report
}
pub fn reset(&mut self) {
self.stats.clear();
}
pub fn merge(&mut self, other: &PassStatistics) {
for (name, rec) in &other.stats {
let entry = self.stats.entry(name.clone()).or_default();
entry.runs += rec.runs;
entry.changes += rec.changes;
entry.functions += rec.functions;
entry.insts_eliminated += rec.insts_eliminated;
entry.blocks_modified += rec.blocks_modified;
entry.total_time_ns += rec.total_time_ns;
}
}
}
#[derive(Debug, Clone)]
pub struct OptBisect {
pub enabled: bool,
pub limit: usize,
next_index: usize,
pass_indices: HashMap<String, usize>,
pub description: String,
}
impl OptBisect {
pub fn new() -> Self {
Self {
enabled: false,
limit: usize::MAX,
next_index: 0,
pass_indices: HashMap::new(),
description: String::new(),
}
}
pub fn enable(&mut self, limit: usize, description: &str) {
self.enabled = true;
self.limit = limit;
self.description = description.to_string();
}
pub fn disable(&mut self) {
self.enabled = false;
self.limit = usize::MAX;
}
pub fn should_skip(&mut self, pass_name: &str) -> bool {
if !self.enabled {
return false;
}
let index = *self
.pass_indices
.entry(pass_name.to_string())
.or_insert_with(|| {
let idx = self.next_index;
self.next_index += 1;
idx
});
index >= self.limit
}
pub fn pass_index(&self, pass_name: &str) -> Option<usize> {
self.pass_indices.get(pass_name).copied()
}
pub fn pass_count(&self) -> usize {
self.next_index
}
pub fn reset(&mut self) {
self.enabled = false;
self.limit = usize::MAX;
self.next_index = 0;
self.pass_indices.clear();
self.description.clear();
}
pub fn status(&self) -> String {
if !self.enabled {
return "OptBisect is disabled.".to_string();
}
format!(
"OptBisect: limit={} passes_indexed={} description='{}'",
self.limit, self.next_index, self.description,
)
}
}
impl Default for OptBisect {
fn default() -> Self {
Self::new()
}
}
#[derive(Default)]
pub struct StandardInstrumentations {
pub time_passes: TimePassesHandler,
pub statistics: PassStatistics,
pub opt_bisect: OptBisect,
pub callbacks: PassInstrumentationCallbacks,
pub debug_passes: bool,
pub verify_each: bool,
}
impl StandardInstrumentations {
pub fn new() -> Self {
Self::default()
}
pub fn enable_timing(&mut self) {
self.time_passes.enabled = true;
}
pub fn enable_statistics(&mut self) {
self.statistics.enabled = true;
}
pub fn enable_bisect(&mut self, limit: usize, description: &str) {
self.opt_bisect.enable(limit, description);
}
pub fn enable_debug_passes(&mut self) {
self.debug_passes = true;
}
pub fn enable_verify_each(&mut self) {
self.verify_each = true;
}
pub fn before_pass_module(&mut self, pass_name: &str, module: &Module) {
if self.debug_passes {
eprintln!("Running pass '{}' on module '{}'", pass_name, module.name);
}
self.callbacks.run_before_pass_module(pass_name, module);
if self.time_passes.enabled {
self.time_passes.start_pass(pass_name);
}
if self.opt_bisect.enabled && self.opt_bisect.should_skip(pass_name) {
self.callbacks.run_pass_skipped(pass_name);
if self.debug_passes {
eprintln!(" Skipping (bisect limit {})", self.opt_bisect.limit);
}
}
}
pub fn after_pass_module(&mut self, pass_name: &str, module: &Module, changed: bool) {
if self.time_passes.enabled {
self.time_passes.stop_pass();
}
self.statistics.record_run(pass_name, changed);
self.callbacks
.run_after_pass_module(pass_name, module, changed);
if self.debug_passes && changed {
eprintln!(" Pass '{}' changed the module.", pass_name);
}
}
pub fn before_pass_function(&mut self, pass_name: &str, func: &ValueRef) {
if self.debug_passes {
let f = func.borrow();
eprintln!("Running pass '{}' on function '{}'", pass_name, f.name);
}
self.callbacks.run_before_pass_function(pass_name, func);
self.statistics.record_function(pass_name);
if self.time_passes.enabled {
self.time_passes.start_pass(pass_name);
}
}
pub fn after_pass_function(&mut self, pass_name: &str, func: &ValueRef, changed: bool) {
if self.time_passes.enabled {
self.time_passes.stop_pass();
}
self.statistics.record_run(pass_name, changed);
self.callbacks
.run_after_pass_function(pass_name, func, changed);
if self.debug_passes && changed {
let f = func.borrow();
eprintln!(" Pass '{}' changed function '{}'.", pass_name, f.name);
}
}
pub fn report(&self) -> String {
let mut report = String::new();
report.push_str(&self.time_passes.report());
report.push('\n');
report.push_str(&self.statistics.report());
report.push('\n');
report.push_str(&self.opt_bisect.status());
report
}
pub fn reset(&mut self) {
self.time_passes.reset();
self.statistics.reset();
self.opt_bisect.reset();
self.callbacks.clear();
}
}
impl std::fmt::Debug for StandardInstrumentations {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("StandardInstrumentations")
.field("time_passes_enabled", &self.time_passes.enabled)
.field("statistics_enabled", &self.statistics.enabled)
.field("opt_bisect_enabled", &self.opt_bisect.enabled)
.field("debug_passes", &self.debug_passes)
.field("verify_each", &self.verify_each)
.finish()
}
}
pub struct InstrumentedPassManager {
pass_manager: PassManager,
pub instrumentation: StandardInstrumentations,
function_pass_names: Vec<String>,
module_pass_names: Vec<String>,
}
impl InstrumentedPassManager {
pub fn new() -> Self {
Self {
pass_manager: PassManager::new(),
instrumentation: StandardInstrumentations::new(),
function_pass_names: Vec::new(),
module_pass_names: Vec::new(),
}
}
pub fn add_function_pass(&mut self, pass: Box<dyn FunctionPass>) {
let name = pass.name().to_string();
self.function_pass_names.push(name);
self.pass_manager.add_function_pass(pass);
}
pub fn add_module_pass(&mut self, pass: Box<dyn ModulePass>) {
let name = pass.name().to_string();
self.module_pass_names.push(name);
self.pass_manager.add_module_pass(pass);
}
pub fn pass_manager(&self) -> &PassManager {
&self.pass_manager
}
pub fn pass_manager_mut(&mut self) -> &mut PassManager {
&mut self.pass_manager
}
pub fn run(&mut self, module: &mut Module) -> bool {
let mut any_change = false;
for name in &self.module_pass_names.clone() {
if self.instrumentation.opt_bisect.should_skip(name) {
self.instrumentation.callbacks.run_pass_skipped(name);
continue;
}
self.instrumentation.before_pass_module(name, module);
let changed = self.pass_manager.run_pipeline(module, &[name.clone()]);
if changed {
any_change = true;
}
self.instrumentation
.after_pass_module(name, module, changed);
}
for name in &self.function_pass_names.clone() {
if self.instrumentation.opt_bisect.should_skip(name) {
self.instrumentation.callbacks.run_pass_skipped(name);
continue;
}
let functions: Vec<ValueRef> = module.functions.clone();
for func in &functions {
if func.borrow().subclass == llvm_native_core::value::SubclassKind::Function {
self.instrumentation.before_pass_function(name, func);
let changed = self.pass_manager.run_pipeline(module, &[name.clone()]);
if changed {
any_change = true;
}
self.instrumentation
.after_pass_function(name, func, changed);
}
}
}
any_change
}
pub fn run_pipeline(&mut self, module: &mut Module, pipeline: &[String]) -> bool {
let mut any_change = false;
for pass_name in pipeline {
if self.instrumentation.opt_bisect.should_skip(pass_name) {
self.instrumentation.callbacks.run_pass_skipped(pass_name);
continue;
}
self.instrumentation.before_pass_module(pass_name, module);
let changed = self.pass_manager.run_pipeline(module, &[pass_name.clone()]);
if changed {
any_change = true;
}
self.instrumentation
.after_pass_module(pass_name, module, changed);
}
any_change
}
pub fn reset(&mut self) {
self.pass_manager = PassManager::new();
self.function_pass_names.clear();
self.module_pass_names.clear();
self.instrumentation.reset();
}
}
impl Default for InstrumentedPassManager {
fn default() -> Self {
Self::new()
}
}
pub struct PassPipelineBuilder {
manager: InstrumentedPassManager,
opt_level: OptimizationLevel,
}
impl PassPipelineBuilder {
pub fn new(opt_level: OptimizationLevel) -> Self {
Self {
manager: InstrumentedPassManager::new(),
opt_level,
}
}
pub fn with_timing(mut self) -> Self {
self.manager.instrumentation.enable_timing();
self
}
pub fn with_statistics(mut self) -> Self {
self.manager.instrumentation.enable_statistics();
self
}
pub fn with_debug_passes(mut self) -> Self {
self.manager.instrumentation.enable_debug_passes();
self
}
pub fn with_verify_each(mut self) -> Self {
self.manager.instrumentation.enable_verify_each();
self
}
pub fn add_function_pass_by_name(mut self, name: &str, registry: &PassRegistry) -> Self {
let _ = registry.get(name);
self
}
pub fn build(self) -> InstrumentedPassManager {
self.manager
}
pub fn opt_level(&self) -> OptimizationLevel {
self.opt_level
}
}
#[cfg(test)]
mod tests {
use super::*;
use llvm_native_core::context::LLVMContext;
use llvm_native_core::function;
use llvm_native_core::instruction;
use llvm_native_core::module::Module;
struct TestPass;
impl Pass for TestPass {
fn name(&self) -> &'static str {
"test-pass"
}
}
impl FunctionPass for TestPass {
fn run_on_function(&mut self, _func: &ValueRef, _am: &AnalysisManager) -> PassResult {
PassResult::Unchanged
}
}
#[test]
fn test_pass_manager_create() {
let mut pm = PassManager::new();
pm.add_function_pass(Box::new(TestPass));
let mut ctx = LLVMContext::new();
let mut module = Module::new("test");
module.set_target_triple("x86_64-unknown-linux-gnu");
let f = function::new_function("main", ctx.i32(), &[]);
module.add_function(f);
let changed = pm.run(&mut module);
assert!(!changed);
}
#[test]
fn test_pass_manager_multiple_passes() {
let mut pm = PassManager::new();
pm.add_function_pass(Box::new(TestPass));
pm.add_function_pass(Box::new(TestPass));
pm.add_function_pass(Box::new(TestPass));
let mut ctx = LLVMContext::new();
let mut module = Module::new("test");
module.set_target_triple("x86_64");
let f = function::new_function("main", ctx.i32(), &[]);
module.add_function(f);
assert!(pm.run(&mut module) == false); }
#[test]
fn test_pass_manager_with_body() {
let mut pm = PassManager::new();
pm.add_function_pass(Box::new(TestPass));
let mut ctx = LLVMContext::new();
let i32_ty = ctx.i32();
let mut module = Module::new("test");
module.set_target_triple("x86_64");
let func = function::new_function("main", i32_ty, &[]);
module.add_function(func.clone());
let entry = llvm_native_core::basic_block::new_basic_block("entry");
let ret = instruction::ret_void();
func.borrow_mut().push_operand(entry.clone());
entry.borrow_mut().push_operand(ret);
let changed = pm.run(&mut module);
assert!(!changed);
}
#[test]
fn test_analysis_manager_cache() {
let mut am = AnalysisManager::new();
assert!(am.get_analysis::<DominatorTree>().is_none());
let mut ctx = LLVMContext::new();
let func = function::new_function("test", ctx.i32(), &[]);
let dt = DominatorTree::compute(&func);
am.set_analysis(dt);
assert!(am.get_analysis::<DominatorTree>().is_some());
am.invalidate::<DominatorTree>();
assert!(am.get_analysis::<DominatorTree>().is_none());
}
#[test]
fn test_pass_manager_stats() {
let mut pm = PassManager::new();
pm.add_function_pass(Box::new(TestPass));
let mut ctx = LLVMContext::new();
let mut module = Module::new("test");
module.set_target_triple("x86_64");
let f = function::new_function("main", ctx.i32(), &[]);
module.add_function(f);
pm.run(&mut module);
assert!(pm.analysis_manager.stats.contains_key("test-pass"));
}
#[test]
fn test_module_pass_adapter() {
struct CountingModulePass {
count: usize,
}
impl Pass for CountingModulePass {
fn name(&self) -> &'static str {
"counting-module-pass"
}
}
impl ModulePass for CountingModulePass {
fn run_on_module(
&mut self,
_module: &mut Module,
_am: &mut AnalysisManager,
) -> PassResult {
self.count += 1;
PassResult::Changed
}
}
let mut pm = PassManager::new();
pm.add_module_pass(Box::new(CountingModulePass { count: 0 }));
let _ctx = LLVMContext::new();
let mut module = Module::new("test");
module.set_target_triple("x86_64");
let changed = pm.run(&mut module);
assert!(changed);
}
#[test]
fn test_typed_analysis_create() {
let analysis = TypedAnalysis::new(42i32, "my-analysis");
assert_eq!(analysis.name(), "my-analysis");
assert_eq!(analysis.value, 42);
assert!(!analysis.invalidate_on("dce"));
}
#[test]
fn test_typed_analysis_invalidation() {
let analysis = TypedAnalysis::new(100u64, "loops")
.add_invalidator("simplifycfg")
.add_invalidator("inline");
assert!(analysis.invalidate_on("simplifycfg"));
assert!(analysis.invalidate_on("inline"));
assert!(!analysis.invalidate_on("dce"));
}
#[test]
fn test_analysis_manager_named() {
let mut am = AnalysisManager::new();
let analysis =
TypedAnalysis::new(vec![1, 2, 3], "test-analysis").add_invalidator("destructive-pass");
am.set_named_analysis("test", analysis);
let retrieved = am.get_analysis_typed::<TypedAnalysis<Vec<i32>>>("test");
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().value, vec![1, 2, 3]);
}
#[test]
fn test_analysis_manager_invalidate_by_pass() {
let mut am = AnalysisManager::new();
am.set_named_analysis(
"loops",
TypedAnalysis::new(1u32, "loops").add_invalidator("simplifycfg"),
);
am.set_named_analysis(
"domtree",
TypedAnalysis::new(2u32, "domtree").add_invalidator("simplifycfg"),
);
am.set_named_analysis(
"callgraph",
TypedAnalysis::new(3u32, "callgraph"), );
am.invalidate_by_pass("simplifycfg");
assert!(am.get_named_analysis("loops").is_none());
assert!(am.get_named_analysis("domtree").is_none());
assert!(am.get_named_analysis("callgraph").is_some());
}
#[test]
fn test_analysis_manager_preserved() {
let mut am = AnalysisManager::new();
am.mark_preserved("domtree");
assert!(am.is_preserved("domtree"));
assert!(!am.is_preserved("loops"));
am.clear_preserved();
assert!(!am.is_preserved("domtree"));
}
#[test]
fn test_pass_requirement_required() {
let req = PassRequirement::required("domtree");
assert_eq!(req.analysis_name, "domtree");
assert!(req.required);
}
#[test]
fn test_pass_requirement_optional() {
let req = PassRequirement::optional("loops");
assert_eq!(req.analysis_name, "loops");
assert!(!req.required);
}
#[test]
fn test_optimization_level_as_str() {
assert_eq!(OptimizationLevel::O0.as_str(), "O0");
assert_eq!(OptimizationLevel::O3.as_str(), "O3");
assert_eq!(OptimizationLevel::Oz.as_str(), "Oz");
}
#[test]
fn test_optimization_level_is_optimizing() {
assert!(!OptimizationLevel::O0.is_optimizing());
assert!(OptimizationLevel::O1.is_optimizing());
assert!(OptimizationLevel::O2.is_optimizing());
}
#[test]
fn test_optimization_level_is_aggressive() {
assert!(!OptimizationLevel::O1.is_aggressive());
assert!(OptimizationLevel::O2.is_aggressive());
assert!(OptimizationLevel::O3.is_aggressive());
}
#[test]
fn test_pass_info_create() {
let info = PassInfo::new("dce", "Dead Code Elimination")
.set_transform()
.with_timing(PassTiming::Early)
.add_preserved("domtree");
assert_eq!(info.name, "dce");
assert!(info.is_transform);
assert!(!info.is_analysis);
assert_eq!(info.timing, PassTiming::Early);
assert!(info.preserves.contains(&"domtree".to_string()));
}
#[test]
fn test_pass_info_analysis() {
let info = PassInfo::new("domtree", "Dominator Tree").set_analysis();
assert!(info.is_analysis);
assert!(!info.is_transform);
}
#[test]
fn test_pass_registry_create() {
let registry = PassRegistry::new();
assert!(registry.is_empty());
assert_eq!(registry.len(), 0);
}
#[test]
fn test_pass_registry_register() {
let mut registry = PassRegistry::new();
registry.register(PassInfo::new("dce", "Dead Code Elimination"));
assert_eq!(registry.len(), 1);
assert!(registry.has("dce"));
assert!(!registry.has("nonexistent"));
}
#[test]
fn test_pass_registry_default_passes() {
let mut registry = PassRegistry::new();
registry.register_default_passes();
assert!(registry.len() > 5);
assert!(registry.has("dce"));
assert!(registry.has("instcombine"));
assert!(registry.has("domtree"));
}
#[test]
fn test_pass_registry_get_pipeline() {
let mut registry = PassRegistry::new();
registry.register_default_passes();
let pipeline = registry.get_pipeline(OptimizationLevel::O2);
assert!(!pipeline.is_empty());
assert!(pipeline.contains(&"dce".to_string()));
}
#[test]
fn test_pass_registry_collect_requirements() {
let mut registry = PassRegistry::new();
registry.register_default_passes();
let reqs = registry.collect_requirements(&["instcombine".to_string()]);
assert!(reqs.contains(&"domtree".to_string()));
}
#[test]
fn test_pass_registry_validate_pipeline() {
let mut registry = PassRegistry::new();
registry.register(
PassInfo::new("my-pass", "A test pass")
.set_transform()
.add_required("domtree"),
);
registry.register(PassInfo::new("domtree", "Dominator Tree").set_analysis());
let missing = registry.validate_pipeline(&["my-pass".to_string()]);
assert!(missing.is_empty(), "domtree should be available");
let missing2 =
registry.validate_pipeline(&["my-pass".to_string(), "unknown-pass".to_string()]);
assert!(missing2.is_empty());
}
#[test]
fn test_pass_registry_remove() {
let mut registry = PassRegistry::new();
registry.register(PassInfo::new("dce", "DCE"));
assert!(registry.has("dce"));
let removed = registry.remove("dce");
assert!(removed.is_some());
assert!(!registry.has("dce"));
}
#[test]
fn test_pass_registry_clear() {
let mut registry = PassRegistry::new();
registry.register_default_passes();
assert!(!registry.is_empty());
registry.clear();
assert!(registry.is_empty());
}
struct CountingTransformPass {
_count: usize,
}
impl TransformPass for CountingTransformPass {
fn name(&self) -> &str {
"counting-transform"
}
fn run_on_function(&mut self, _func: &ValueRef, _am: &mut AnalysisManager) -> bool {
false
}
fn get_requirements(&self) -> Vec<PassRequirement> {
vec![PassRequirement::required("domtree")]
}
fn get_preserved_analyses(&self) -> Vec<String> {
vec!["callgraph".to_string()]
}
fn is_function_pass(&self) -> bool {
true
}
}
#[test]
fn test_transform_pass_name() {
let pass = CountingTransformPass { _count: 0 };
assert_eq!(pass.name(), "counting-transform");
}
#[test]
fn test_transform_pass_requirements() {
let pass = CountingTransformPass { _count: 0 };
let reqs = pass.get_requirements();
assert_eq!(reqs.len(), 1);
assert_eq!(reqs[0].analysis_name, "domtree");
assert!(reqs[0].required);
}
#[test]
fn test_transform_pass_preserved() {
let pass = CountingTransformPass { _count: 0 };
let preserved = pass.get_preserved_analyses();
assert!(preserved.contains(&"callgraph".to_string()));
}
#[test]
fn test_pass_manager_debug_flag() {
let mut pm = PassManager::new();
assert!(!pm.debug);
pm.debug = true;
assert!(pm.debug);
}
#[test]
fn test_pass_manager_time_passes() {
let mut pm = PassManager::new();
pm.time_passes = true;
pm.add_function_pass(Box::new(TestPass));
let mut ctx = LLVMContext::new();
let mut module = Module::new("test");
module.set_target_triple("x86_64");
let f = function::new_function("main", ctx.i32(), &[]);
module.add_function(f);
pm.run(&mut module);
assert_eq!(pm.pass_timings.len(), 1);
}
#[test]
fn test_pass_manager_run_pipeline() {
let mut pm = PassManager::new();
pm.add_function_pass(Box::new(TestPass));
let mut ctx = LLVMContext::new();
let mut module = Module::new("test");
module.set_target_triple("x86_64");
let f = function::new_function("main", ctx.i32(), &[]);
module.add_function(f);
let changed = pm.run_pipeline(&mut module, &["test-pass".to_string()]);
assert!(!changed);
}
#[test]
fn test_pass_manager_analysis_manager_access() {
let pm = PassManager::new();
let _am = pm.analysis_manager();
assert!(_am.stats.is_empty());
}
}