use std::collections::BTreeMap;
pub mod path {
pub const AOT: u8 = 1 << 0;
pub const RUN: u8 = 1 << 1;
pub const VM: u8 = 1 << 2;
pub const JIT: u8 = 1 << 3;
pub const CODEGEN: u8 = 1 << 4;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MemClass {
Neutral,
TradesMemForSpeed,
SavesMem,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum OptCost {
Cheap = 0,
Medium = 1,
Heavy = 2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Tier {
T0 = 0,
T1 = 1,
T2 = 2,
T3 = 3,
}
impl Tier {
#[inline]
pub fn budget(self) -> Option<OptCost> {
match self {
Tier::T0 => None,
Tier::T1 => Some(OptCost::Cheap),
Tier::T2 => Some(OptCost::Medium),
Tier::T3 => Some(OptCost::Heavy),
}
}
}
#[inline]
pub fn admits(cfg: &OptimizationConfig, tier: Tier, opt: Opt) -> bool {
matches!(tier.budget(), Some(b) if opt.cost() <= b) && cfg.is_on(opt)
}
pub const OPT_COUNT: usize = Opt::Supercompile as usize + 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TierMode {
Tiered,
Eager,
Baseline,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TierThresholds {
pub t1: u32,
pub t2: u32,
pub t3: u32,
}
impl Default for TierThresholds {
fn default() -> Self {
Self { t1: 8, t2: 32, t3: 100 }
}
}
impl TierThresholds {
#[inline]
pub fn tier_for(self, count: u32) -> Tier {
if count >= self.t3 {
Tier::T3
} else if count >= self.t2 {
Tier::T2
} else if count >= self.t1 {
Tier::T1
} else {
Tier::T0
}
}
pub fn from_env() -> Self {
Self::from_spec(
std::env::var("LOGOS_TIER_THRESHOLDS").ok().as_deref(),
std::env::var("LOGOS_TIER_T1").ok().as_deref(),
std::env::var("LOGOS_TIER_T2").ok().as_deref(),
std::env::var("LOGOS_TIER_T3").ok().as_deref(),
)
}
pub fn from_spec(
combined: Option<&str>,
t1: Option<&str>,
t2: Option<&str>,
t3: Option<&str>,
) -> Self {
let mut th = Self::default();
if let Some(c) = combined {
let parts: Vec<u32> = c.split(',').filter_map(|s| s.trim().parse().ok()).collect();
if parts.len() == 3 {
th = Self { t1: parts[0], t2: parts[1], t3: parts[2] };
}
}
if let Some(v) = t1.and_then(|s| s.trim().parse().ok()) {
th.t1 = v;
}
if let Some(v) = t2.and_then(|s| s.trim().parse().ok()) {
th.t2 = v;
}
if let Some(v) = t3.and_then(|s| s.trim().parse().ok()) {
th.t3 = v;
}
th
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Pin {
Never,
Eager,
At(Tier),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PinSet {
pins: [Option<Pin>; OPT_COUNT],
}
impl Default for PinSet {
fn default() -> Self {
Self::none()
}
}
impl PinSet {
pub const fn none() -> Self {
Self { pins: [None; OPT_COUNT] }
}
#[inline]
pub fn get(&self, opt: Opt) -> Option<Pin> {
self.pins[opt as usize]
}
#[inline]
pub fn set(&mut self, opt: Opt, pin: Pin) {
self.pins[opt as usize] = Some(pin);
}
pub fn overlay(&mut self, other: &PinSet) {
for (slot, pin) in self.pins.iter_mut().zip(other.pins.iter()) {
if let Some(p) = pin {
*slot = Some(*p);
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HotswapConfig {
pub mode: TierMode,
pub thresholds: TierThresholds,
pub force_tier: Option<Tier>,
pub pins: PinSet,
}
impl Default for HotswapConfig {
fn default() -> Self {
Self {
mode: TierMode::Eager,
thresholds: TierThresholds::default(),
force_tier: None,
pins: PinSet::none(),
}
}
}
impl HotswapConfig {
#[inline]
pub fn effective_tier(&self, count: u32) -> Tier {
if let Some(t) = self.force_tier {
return t;
}
match self.mode {
TierMode::Baseline => Tier::T0,
TierMode::Eager => Tier::T3,
TierMode::Tiered => self.thresholds.tier_for(count),
}
}
#[inline]
pub fn pin(&self, opt: Opt) -> Option<Pin> {
self.pins.get(opt)
}
#[inline]
pub fn run_tier(&self) -> Tier {
if let Some(t) = self.force_tier {
return t;
}
match self.mode {
TierMode::Eager => Tier::T3,
TierMode::Tiered | TierMode::Baseline => Tier::T0,
}
}
pub fn from_env() -> Self {
Self::from_spec(
std::env::var("LOGOS_HOTSWAP").ok().as_deref(),
std::env::var("LOGOS_TIER_PROFILE").ok().as_deref(),
std::env::var("LOGOS_FORCE_TIER").ok().as_deref(),
TierThresholds::from_env(),
std::env::var("LOGOS_TIER_PIN").ok().as_deref(),
)
}
pub fn from_spec(
hotswap: Option<&str>,
profile: Option<&str>,
force_tier: Option<&str>,
thresholds: TierThresholds,
pin_spec: Option<&str>,
) -> Self {
let mode = if hotswap == Some("off") {
TierMode::Eager
} else {
match profile {
Some("tiered") => TierMode::Tiered,
Some("baseline") => TierMode::Baseline,
_ => TierMode::Eager,
}
};
let force_tier = force_tier.and_then(parse_tier);
let mut pins = PinSet::none();
if let Some(spec) = pin_spec {
apply_pin_spec(&mut pins, spec);
}
HotswapConfig { mode, thresholds, force_tier, pins }
}
}
fn parse_tier(s: &str) -> Option<Tier> {
match s.trim().to_ascii_lowercase().as_str() {
"0" | "t0" | "baseline" => Some(Tier::T0),
"1" | "t1" | "warm" => Some(Tier::T1),
"2" | "t2" | "hot" => Some(Tier::T2),
"3" | "t3" | "veryhot" | "very_hot" => Some(Tier::T3),
_ => None,
}
}
pub fn pin_from_str(s: &str) -> Option<Pin> {
match s.trim().to_ascii_lowercase().as_str() {
"eager" => Some(Pin::Eager),
"never" => Some(Pin::Never),
other => parse_tier(other).map(Pin::At),
}
}
fn apply_pin_spec(pins: &mut PinSet, spec: &str) {
for tok in spec.split([',', ';']) {
let tok = tok.trim();
if tok.is_empty() {
continue;
}
if let Some((kw, val)) = tok.split_once(':') {
if let (Some(opt), Some(pin)) = (by_keyword(kw.trim()), pin_from_str(val)) {
pins.set(opt, pin);
}
}
}
}
#[inline]
pub fn admits_pinned(cfg: &OptimizationConfig, hs: &HotswapConfig, tier: Tier, opt: Opt) -> bool {
if !cfg.is_on(opt) {
return false;
}
match hs.pin(opt) {
Some(Pin::Never) => false,
Some(Pin::Eager) => true,
Some(Pin::At(t)) => tier >= t,
None => matches!(tier.budget(), Some(b) if opt.cost() <= b),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Scope {
ProgramOnly,
Both,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum Opt {
Memo = 0,
Tco = 1,
Peephole = 2,
Borrow = 3,
Specialize = 4,
Comptime = 5,
Inline = 6,
Unfold = 7,
Defunctionalize = 8,
LoopHoist = 9,
Unroll = 10,
LoopSplit = 11,
ClosedForm = 12,
Fuse = 13,
LoopCse = 14,
Cse = 15,
DeadCode = 16,
Scalarize = 17,
Affine = 18,
Interleave = 19,
Unbox = 20,
HoistBorrows = 21,
Narrow = 22,
NarrowVm = 23,
NarrowMap = 24,
DenseMap = 25,
ElemType = 26,
FastDiv = 27,
FloatStrength = 28,
Oracle = 29,
OracleHints = 30,
Unchecked = 31,
Simd = 32,
Cascade = 33,
IndexString = 34,
CapScale = 35,
Symmetry = 36,
Popcount = 37,
Saturate = 38,
Supercompile = 39,
}
impl Opt {
#[inline]
pub const fn bit(self) -> u64 {
1u64 << (self as u8)
}
pub fn meta(self) -> &'static OptMeta {
®ISTRY[self as usize]
}
#[inline]
pub fn cost(self) -> OptCost {
self.meta().cost
}
}
#[derive(Debug, Clone, Copy)]
pub struct OptMeta {
pub opt: Opt,
pub keyword: &'static str,
pub label: &'static str,
pub group: &'static str,
pub default_on: bool,
pub paths: u8,
pub emits_unsafe: bool,
pub mem_class: MemClass,
pub cost: OptCost,
pub requires: &'static [Opt],
pub conflicts: &'static [Opt],
pub preempts: &'static [Opt],
pub scope: Scope,
}
use path::{AOT, CODEGEN, JIT, RUN, VM};
use MemClass::{Neutral, SavesMem, TradesMemForSpeed};
use OptCost::{Cheap, Heavy, Medium};
use Scope::{Both, ProgramOnly};
pub static REGISTRY: &[OptMeta] = &[
OptMeta { opt: Opt::Memo, keyword: "memo", label: "Memoization", group: "Inlining & calls", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: TradesMemForSpeed, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Tco, keyword: "tco", label: "Tail-call optimization", group: "Inlining & calls", default_on: true, paths: AOT | RUN | CODEGEN, emits_unsafe: false, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[Opt::Memo], scope: Both },
OptMeta { opt: Opt::Peephole, keyword: "peephole", label: "Peephole rewrites", group: "Peephole", default_on: true, paths: AOT | CODEGEN, emits_unsafe: true, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Borrow, keyword: "borrow", label: "Borrow inference", group: "Arrays & memory", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: SavesMem, cost: Cheap, requires: &[], conflicts: &[], preempts: &[Opt::Tco], scope: Both },
OptMeta { opt: Opt::Specialize, keyword: "specialize", label: "Partial evaluation", group: "Inlining & calls", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: TradesMemForSpeed, cost: Medium, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Comptime, keyword: "comptime", label: "Compile-time evaluation (CTFE)", group: "Inlining & calls", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: Neutral, cost: Medium, requires: &[], conflicts: &[], preempts: &[Opt::Supercompile], scope: Both },
OptMeta { opt: Opt::Inline, keyword: "inline", label: "Function inlining", group: "Inlining & calls", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: TradesMemForSpeed, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Unfold, keyword: "unfold", label: "Recursion unrolling", group: "Inlining & calls", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: TradesMemForSpeed, cost: Heavy, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Defunctionalize, keyword: "defunctionalize", label: "Defunctionalization", group: "Inlining & calls", default_on: true, paths: AOT, emits_unsafe: false, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::LoopHoist, keyword: "loophoist", label: "Loop-invariant code motion", group: "Loops", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: Neutral, cost: Medium, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Unroll, keyword: "unroll", label: "Loop unrolling", group: "Loops", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: TradesMemForSpeed, cost: Heavy, requires: &[], conflicts: &[], preempts: &[Opt::Interleave], scope: Both },
OptMeta { opt: Opt::LoopSplit, keyword: "loopsplit", label: "Loop index-set splitting", group: "Loops", default_on: true, paths: AOT, emits_unsafe: false, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::ClosedForm, keyword: "closedform", label: "Closed-form loop recognition", group: "Loops", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: Neutral, cost: Medium, requires: &[], conflicts: &[], preempts: &[Opt::Peephole], scope: Both },
OptMeta { opt: Opt::Fuse, keyword: "fuse", label: "Deforestation (stream fusion)", group: "Loops", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: SavesMem, cost: Medium, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::LoopCse, keyword: "loopcse", label: "Loop-carried CSE", group: "Loops", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: Neutral, cost: Medium, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Cse, keyword: "cse", label: "Common-subexpression elimination (GVN)", group: "Redundancy", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: Neutral, cost: Medium, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::DeadCode, keyword: "deadcode", label: "Dead-code elimination", group: "Redundancy", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: SavesMem, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Scalarize, keyword: "scalarize", label: "Array scalarization (SROA)", group: "Arrays & memory", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: TradesMemForSpeed, cost: Heavy, requires: &[Opt::Cse], conflicts: &[], preempts: &[Opt::HoistBorrows, Opt::Interleave, Opt::Unbox], scope: Both },
OptMeta { opt: Opt::Affine, keyword: "affine", label: "Affine array → closed form", group: "Arrays & memory", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: SavesMem, cost: Medium, requires: &[Opt::Unbox], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Interleave, keyword: "interleave", label: "Array-of-structs interleaving", group: "Arrays & memory", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: Neutral, cost: Heavy, requires: &[Opt::Scalarize], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::Unbox, keyword: "unbox", label: "De-Rc (Vec instead of Rc<RefCell>)", group: "Arrays & memory", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: SavesMem, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::HoistBorrows, keyword: "hoistborrows", label: "Borrow hoisting", group: "Arrays & memory", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::Narrow, keyword: "narrow", label: "i32 sequence narrowing (codegen)", group: "Number representation", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: SavesMem, cost: Cheap, requires: &[Opt::Unbox], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::NarrowVm, keyword: "narrowvm", label: "i32 sequence narrowing (VM)", group: "Number representation", default_on: true, paths: VM | RUN, emits_unsafe: false, mem_class: SavesMem, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::NarrowMap, keyword: "narrowmap", label: "i32 map-key narrowing", group: "Number representation", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: SavesMem, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::DenseMap, keyword: "densemap", label: "Dense direct-addressed map", group: "Number representation", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: TradesMemForSpeed, cost: Cheap, requires: &[], conflicts: &[], preempts: &[Opt::NarrowMap], scope: ProgramOnly },
OptMeta { opt: Opt::ElemType, keyword: "elemtype", label: "Element-type narrowing", group: "Number representation", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: SavesMem, cost: Medium, requires: &[Opt::Oracle], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::FastDiv, keyword: "fastdiv", label: "Magic-reciprocal division", group: "Number representation", default_on: true, paths: AOT | VM | CODEGEN, emits_unsafe: false, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::FloatStrength, keyword: "floatstrength", label: "Float induction strength reduction", group: "Number representation", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: Neutral, cost: Medium, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Oracle, keyword: "oracle", label: "Abstract interpretation (oracle facts)", group: "Bounds & checks", default_on: true, paths: AOT | RUN, emits_unsafe: false, mem_class: Neutral, cost: Medium, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::OracleHints, keyword: "oraclehints", label: "Oracle bounds-check guards", group: "Bounds & checks", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: Neutral, cost: Medium, requires: &[Opt::Oracle], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Unchecked, keyword: "unchecked", label: "Oracle-proven unchecked indexing", group: "Bounds & checks", default_on: true, paths: AOT | JIT | CODEGEN, emits_unsafe: true, mem_class: Neutral, cost: Medium, requires: &[Opt::Oracle], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Simd, keyword: "simd", label: "SIMD search kernels", group: "Strings & SIMD", default_on: true, paths: AOT | CODEGEN, emits_unsafe: true, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::Cascade, keyword: "cascade", label: "Cascade folding", group: "Strings & SIMD", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::IndexString, keyword: "indexstring", label: "Indexed string search", group: "Strings & SIMD", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::CapScale, keyword: "capscale", label: "Capacity-scaling buffer fill", group: "Strings & SIMD", default_on: true, paths: AOT | CODEGEN, emits_unsafe: false, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: ProgramOnly },
OptMeta { opt: Opt::Symmetry, keyword: "symmetry", label: "Symmetry breaking", group: "Search-space", default_on: true, paths: AOT, emits_unsafe: false, mem_class: Neutral, cost: Heavy, requires: &[Opt::Specialize], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Popcount, keyword: "popcount", label: "Popcount leaf collapse", group: "Search-space", default_on: true, paths: AOT, emits_unsafe: false, mem_class: Neutral, cost: Cheap, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Saturate, keyword: "saturate", label: "Equality saturation (e-graph)", group: "Search-space", default_on: true, paths: AOT, emits_unsafe: false, mem_class: Neutral, cost: Heavy, requires: &[], conflicts: &[], preempts: &[], scope: Both },
OptMeta { opt: Opt::Supercompile, keyword: "supercompile", label: "Supercompilation", group: "Search-space", default_on: true, paths: AOT, emits_unsafe: false, mem_class: TradesMemForSpeed, cost: Heavy, requires: &[], conflicts: &[], preempts: &[], scope: Both },
];
pub fn by_keyword(word: &str) -> Option<Opt> {
let w = word.to_ascii_lowercase();
REGISTRY.iter().find(|m| m.keyword == w).map(|m| m.opt)
}
pub fn decorate_source(src: &str, disabled_keywords: &[&str]) -> String {
if disabled_keywords.is_empty() {
return src.to_string();
}
let decorators: String = disabled_keywords
.iter()
.map(|kw| format!("## No {kw}\n"))
.collect();
match src.find("## Main") {
Some(idx) => format!("{}{}{}", &src[..idx], decorators, &src[idx..]),
None => format!("{src}{decorators}"),
}
}
pub fn decorate_tiers(src: &str, pins: &[(&str, &str)]) -> String {
if pins.is_empty() {
return src.to_string();
}
let decorators: String = pins
.iter()
.map(|(kw, val)| format!("## Tier {kw} {val}\n"))
.collect();
match src.find("## Main") {
Some(idx) => format!("{}{}{}", &src[..idx], decorators, &src[idx..]),
None => format!("{src}{decorators}"),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Reason {
DependencyOff(Opt),
ConflictWith(Opt),
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct NormalizeReport {
pub auto_disabled: Vec<(Opt, Reason)>,
}
impl NormalizeReport {
pub fn is_empty(&self) -> bool {
self.auto_disabled.is_empty()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
pub struct OptimizationConfig {
enabled: u64,
}
impl Default for OptimizationConfig {
fn default() -> Self {
Self::all_on()
}
}
impl OptimizationConfig {
pub fn all_on() -> Self {
let mut enabled = 0u64;
for m in REGISTRY {
if m.default_on {
enabled |= m.opt.bit();
}
}
Self { enabled }
}
pub const fn all_off() -> Self {
Self { enabled: 0 }
}
#[inline]
pub const fn is_on(&self, opt: Opt) -> bool {
self.enabled & opt.bit() != 0
}
#[inline]
pub fn set(&mut self, opt: Opt, on: bool) {
if on {
self.enabled |= opt.bit();
} else {
self.enabled &= !opt.bit();
}
}
pub fn disable(mut self, opt: Opt) -> Self {
self.set(opt, false);
self
}
pub fn enable(mut self, opt: Opt) -> Self {
self.set(opt, true);
self
}
pub fn enable_with_requires(&mut self, opt: Opt) {
let mut visited = 0u64;
let mut stack = vec![opt];
while let Some(o) = stack.pop() {
if visited & o.bit() != 0 {
continue;
}
visited |= o.bit();
self.set(o, true);
for &req in o.meta().requires {
stack.push(req);
}
}
}
pub const fn is_all_off(&self) -> bool {
self.enabled == 0
}
pub const fn bits(&self) -> u64 {
self.enabled
}
pub const fn merged(&self, func: &OptimizationConfig) -> OptimizationConfig {
OptimizationConfig { enabled: self.enabled & func.enabled }
}
pub fn disabled_keywords(&self) -> impl Iterator<Item = &'static str> + '_ {
REGISTRY
.iter()
.filter(move |m| !self.is_on(m.opt))
.map(|m| m.keyword)
}
pub fn normalize(&mut self) -> NormalizeReport {
let mut report = NormalizeReport::default();
if self.is_all_off() {
return report;
}
for m in REGISTRY {
if !self.is_on(m.opt) {
continue;
}
for &other in m.conflicts {
if (other as u8) < (m.opt as u8) && self.is_on(other) {
self.set(m.opt, false);
report.auto_disabled.push((m.opt, Reason::ConflictWith(other)));
break;
}
}
}
loop {
let mut changed = false;
for m in REGISTRY {
if !self.is_on(m.opt) {
continue;
}
for &req in m.requires {
if !self.is_on(req) {
self.set(m.opt, false);
report.auto_disabled.push((m.opt, Reason::DependencyOff(req)));
changed = true;
break;
}
}
}
if !changed {
break;
}
}
report
}
pub fn from_env() -> Self {
let opt = std::env::var("LOGOS_OPT").ok();
let no_opt = std::env::var("LOGOS_NO_OPTIMIZE").ok();
let profile = std::env::var("LOGOS_OPT_PROFILE").ok();
let off = std::env::var("LOGOS_OPT_OFF").ok();
let master_off = opt.as_deref() == Some("off") || no_opt.as_deref() == Some("1");
Self::from_spec(master_off, profile.as_deref(), off.as_deref())
}
pub fn from_spec(master_off: bool, profile: Option<&str>, off_list: Option<&str>) -> Self {
if master_off {
return Self::all_off();
}
let mut cfg = match profile {
Some("memory") => Profile::Memory.config(),
Some("safety") => Profile::Safety.config(),
_ => Profile::Speed.config(),
};
if let Some(list) = off_list {
for tok in list.split([',', ' ', ';']) {
let tok = tok.trim();
if tok.is_empty() {
continue;
}
if let Some(opt) = by_keyword(tok) {
cfg.set(opt, false);
}
}
}
cfg.normalize();
cfg
}
pub fn from_toggles(toggles: &BTreeMap<String, bool>) -> (Self, Vec<Opt>) {
let mut cfg = Self::all_on();
for m in REGISTRY {
if let Some(&on) = toggles.get(m.keyword) {
cfg.set(m.opt, on);
}
}
let before = cfg.enabled;
let report = cfg.normalize();
let _ = before;
let forced = report.auto_disabled.iter().map(|(opt, _)| *opt).collect();
(cfg, forced)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct FiredOptimizations {
fired: u64,
}
impl FiredOptimizations {
pub const fn new() -> Self {
Self { fired: 0 }
}
pub const fn from_bits(fired: u64) -> Self {
Self { fired }
}
#[inline]
pub fn mark(&mut self, opt: Opt) {
self.fired |= opt.bit();
}
#[inline]
pub const fn fired(&self, opt: Opt) -> bool {
self.fired & opt.bit() != 0
}
pub const fn is_empty(&self) -> bool {
self.fired == 0
}
pub const fn bits(&self) -> u64 {
self.fired
}
pub fn merge(&mut self, other: &FiredOptimizations) {
self.fired |= other.fired;
}
pub fn opts(&self) -> Vec<Opt> {
REGISTRY
.iter()
.filter(|m| self.fired(m.opt))
.map(|m| m.opt)
.collect()
}
pub fn keywords(&self) -> Vec<&'static str> {
REGISTRY
.iter()
.filter(|m| self.fired(m.opt))
.map(|m| m.keyword)
.collect()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OptRole {
Fired,
Enabler,
Preempted,
}
#[derive(Debug, Clone)]
pub struct OptNode {
pub opt: Opt,
pub depth: usize,
pub role: OptRole,
pub has_children: bool,
pub requires: Vec<Opt>,
pub preempts: Vec<Opt>,
pub preempted_by: Vec<Opt>,
pub depends_on: Vec<Opt>,
}
pub fn relationship_tree(
fired: &[Opt],
preempted: &[(Opt, Opt)],
dependencies: &[(Opt, Opt)],
) -> Vec<OptNode> {
let n = REGISTRY.len();
let idx = |o: Opt| o as usize;
let parents_of = |y: usize| -> Vec<usize> {
let yopt = REGISTRY[y].opt;
let mut ps: Vec<usize> = REGISTRY[y].requires.iter().map(|&r| idx(r)).collect();
for &(dep, on) in dependencies {
if dep == yopt {
ps.push(idx(on));
}
}
ps.sort_unstable();
ps.dedup();
ps
};
let mut want = vec![false; n];
for &o in fired {
want[idx(o)] = true;
}
for &(_, loser) in preempted {
want[idx(loser)] = true;
}
for &(dep, on) in dependencies {
want[idx(dep)] = true;
want[idx(on)] = true;
}
loop {
let mut changed = false;
for i in 0..n {
if want[i] {
for p in parents_of(i) {
if !want[p] {
want[p] = true;
changed = true;
}
}
}
}
if !changed {
break;
}
}
let fired_set: Vec<bool> = {
let mut v = vec![false; n];
for &o in fired {
v[idx(o)] = true;
}
v
};
let loser_set: Vec<bool> = {
let mut v = vec![false; n];
for &(_, l) in preempted {
v[idx(l)] = true;
}
v
};
let role_of = |i: usize| {
if fired_set[i] {
OptRole::Fired
} else if loser_set[i] {
OptRole::Preempted
} else {
OptRole::Enabler
}
};
let parent_in_want = |i: usize| parents_of(i).iter().any(|&p| want[p]);
let mut order: Vec<(usize, usize)> = Vec::new();
let mut visited = vec![false; n];
let roots: Vec<usize> = (0..n).filter(|&i| want[i] && !parent_in_want(i)).collect();
let mut stack: Vec<(usize, usize)> = roots.iter().rev().map(|&i| (i, 0usize)).collect();
while let Some((i, depth)) = stack.pop() {
if visited[i] {
continue;
}
visited[i] = true;
order.push((i, depth));
let kids: Vec<usize> = (0..n)
.filter(|&j| want[j] && !visited[j] && parents_of(j).contains(&i))
.collect();
for &k in kids.iter().rev() {
stack.push((k, depth + 1));
}
}
for i in 0..n {
if want[i] && !visited[i] {
order.push((i, 0));
}
}
order
.into_iter()
.map(|(i, depth)| {
let opt = REGISTRY[i].opt;
let has_children = (0..n).any(|j| want[j] && parents_of(j).contains(&i));
let preempted_by: Vec<Opt> = preempted
.iter()
.filter(|&&(_, l)| l == opt)
.map(|&(w, _)| w)
.collect();
let depends_on: Vec<Opt> = dependencies
.iter()
.filter(|&&(d, _)| d == opt)
.map(|&(_, x)| x)
.collect();
OptNode {
opt,
depth,
role: role_of(i),
has_children,
requires: REGISTRY[i].requires.to_vec(),
preempts: REGISTRY[i].preempts.to_vec(),
preempted_by,
depends_on,
}
})
.collect()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Profile {
Speed,
Memory,
Safety,
}
impl Profile {
pub fn config(self) -> OptimizationConfig {
let mut cfg = OptimizationConfig::all_on();
match self {
Profile::Speed => {}
Profile::Memory => {
for m in REGISTRY {
if m.mem_class == MemClass::TradesMemForSpeed {
cfg.set(m.opt, false);
}
}
}
Profile::Safety => {
for m in REGISTRY {
if m.emits_unsafe {
cfg.set(m.opt, false);
}
}
}
}
cfg.normalize();
cfg
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn registry_has_one_row_per_opt_in_order() {
for (i, m) in REGISTRY.iter().enumerate() {
assert_eq!(m.opt as usize, i, "registry row {i} out of order: {:?}", m.opt);
}
}
#[test]
fn keywords_are_unique_lowercase_single_tokens() {
let mut seen = HashSet::new();
for m in REGISTRY {
assert!(seen.insert(m.keyword), "duplicate keyword {}", m.keyword);
assert_eq!(m.keyword, m.keyword.to_ascii_lowercase(), "keyword not lowercase: {}", m.keyword);
assert!(!m.keyword.is_empty(), "empty keyword");
assert!(!m.keyword.contains(char::is_whitespace), "keyword has whitespace: {}", m.keyword);
}
}
#[test]
fn requires_and_conflicts_reference_real_variants_no_cycle() {
for m in REGISTRY {
assert!(!m.requires.contains(&m.opt), "{:?} requires itself", m.opt);
assert!(!m.conflicts.contains(&m.opt), "{:?} conflicts with itself", m.opt);
for &r in m.requires {
assert!(!r.meta().requires.contains(&m.opt), "requires cycle: {:?} <-> {:?}", m.opt, r);
}
}
}
#[test]
fn run_path_opt_costs_match_fixture() {
use OptCost::{Cheap, Heavy, Medium};
let expect = |opt: Opt, c: OptCost| assert_eq!(opt.cost(), c, "{opt:?} cost");
for (opt, c) in [
(Opt::Inline, Cheap),
(Opt::Tco, Cheap),
(Opt::DeadCode, Cheap),
(Opt::Specialize, Medium), (Opt::Comptime, Medium),
(Opt::LoopCse, Medium),
(Opt::FloatStrength, Medium),
(Opt::Cse, Medium),
(Opt::Affine, Medium),
(Opt::LoopHoist, Medium),
(Opt::ClosedForm, Medium),
(Opt::Fuse, Medium),
(Opt::Oracle, Medium),
(Opt::ElemType, Medium),
(Opt::Unroll, Heavy),
(Opt::Scalarize, Heavy),
(Opt::Unfold, Heavy),
] {
expect(opt, c);
}
}
#[test]
fn requires_cost_monotone() {
for m in REGISTRY {
for &r in m.requires {
assert!(
r.cost() <= m.opt.cost(),
"{:?} (cost {:?}) requires {:?} (cost {:?}) — violates cost monotonicity",
m.opt,
m.opt.cost(),
r,
r.cost()
);
}
}
}
#[test]
fn tier_budget_and_admits_truth_table() {
use OptCost::{Cheap, Heavy, Medium};
assert_eq!(Tier::T0.budget(), None);
assert_eq!(Tier::T1.budget(), Some(Cheap));
assert_eq!(Tier::T2.budget(), Some(Medium));
assert_eq!(Tier::T3.budget(), Some(Heavy));
let on = OptimizationConfig::all_on();
assert!(!admits(&on, Tier::T0, Opt::Inline));
assert!(admits(&on, Tier::T1, Opt::Inline)); assert!(!admits(&on, Tier::T1, Opt::Cse)); assert!(!admits(&on, Tier::T1, Opt::Unroll)); assert!(admits(&on, Tier::T2, Opt::Inline));
assert!(admits(&on, Tier::T2, Opt::Cse));
assert!(!admits(&on, Tier::T2, Opt::Unroll));
assert!(admits(&on, Tier::T3, Opt::Inline));
assert!(admits(&on, Tier::T3, Opt::Cse));
assert!(admits(&on, Tier::T3, Opt::Unroll));
let off = OptimizationConfig::all_on().disable(Opt::Inline);
assert!(!admits(&off, Tier::T3, Opt::Inline));
assert!(Tier::T0 < Tier::T1 && Tier::T1 < Tier::T2 && Tier::T2 < Tier::T3);
}
#[test]
fn opt_count_matches_registry() {
assert_eq!(OPT_COUNT, REGISTRY.len());
}
#[test]
fn tier_thresholds_default_and_tier_for() {
let t = TierThresholds::default();
assert_eq!((t.t1, t.t2, t.t3), (8, 32, 100));
assert_eq!(t.tier_for(0), Tier::T0);
assert_eq!(t.tier_for(7), Tier::T0);
assert_eq!(t.tier_for(8), Tier::T1);
assert_eq!(t.tier_for(31), Tier::T1);
assert_eq!(t.tier_for(32), Tier::T2);
assert_eq!(t.tier_for(99), Tier::T2);
assert_eq!(t.tier_for(100), Tier::T3);
assert_eq!(t.tier_for(10_000), Tier::T3);
}
#[test]
fn hotswap_mode_effective_tier() {
let th = TierThresholds::default();
let mk = |mode, force_tier| HotswapConfig {
mode,
thresholds: th,
force_tier,
pins: PinSet::none(),
};
assert_eq!(mk(TierMode::Eager, None).effective_tier(0), Tier::T3);
assert_eq!(mk(TierMode::Baseline, None).effective_tier(10_000), Tier::T0);
let tiered = mk(TierMode::Tiered, None);
assert_eq!(tiered.effective_tier(0), Tier::T0);
assert_eq!(tiered.effective_tier(8), Tier::T1);
assert_eq!(tiered.effective_tier(32), Tier::T2);
assert_eq!(tiered.effective_tier(100), Tier::T3);
let forced = mk(TierMode::Tiered, Some(Tier::T2));
assert_eq!(forced.effective_tier(0), Tier::T2);
assert_eq!(forced.effective_tier(10_000), Tier::T2);
}
#[test]
fn hotswap_from_spec_modes_and_force() {
let th = TierThresholds::default();
assert_eq!(
HotswapConfig::from_spec(Some("off"), Some("baseline"), None, th, None).mode,
TierMode::Eager
);
assert_eq!(HotswapConfig::from_spec(None, Some("eager"), None, th, None).mode, TierMode::Eager);
assert_eq!(HotswapConfig::from_spec(None, Some("baseline"), None, th, None).mode, TierMode::Baseline);
assert_eq!(HotswapConfig::from_spec(None, Some("tiered"), None, th, None).mode, TierMode::Tiered);
assert_eq!(HotswapConfig::from_spec(None, None, None, th, None).mode, TierMode::Eager);
assert_eq!(HotswapConfig::default().mode, TierMode::Eager);
assert_eq!(HotswapConfig::from_spec(None, None, Some("2"), th, None).force_tier, Some(Tier::T2));
assert_eq!(HotswapConfig::from_spec(None, None, Some("veryhot"), th, None).force_tier, Some(Tier::T3));
assert_eq!(HotswapConfig::from_spec(None, None, Some("nonsense"), th, None).force_tier, None);
}
#[test]
fn run_tier_maps_mode_to_upfront_tier() {
let th = TierThresholds::default();
let mk = |mode, force_tier| HotswapConfig { mode, thresholds: th, force_tier, pins: PinSet::none() };
assert_eq!(mk(TierMode::Eager, None).run_tier(), Tier::T3);
assert_eq!(mk(TierMode::Tiered, None).run_tier(), Tier::T0);
assert_eq!(mk(TierMode::Baseline, None).run_tier(), Tier::T0);
assert_eq!(mk(TierMode::Tiered, Some(Tier::T2)).run_tier(), Tier::T2);
assert_eq!(mk(TierMode::Eager, Some(Tier::T1)).run_tier(), Tier::T1);
}
#[test]
fn admits_pinned_overrides_cost_tier() {
let on = OptimizationConfig::all_on();
let mut pins = PinSet::none();
pins.set(Opt::Unfold, Pin::Never);
pins.set(Opt::Fuse, Pin::Eager);
pins.set(Opt::Specialize, Pin::At(Tier::T2));
let hs = HotswapConfig {
mode: TierMode::Tiered,
thresholds: TierThresholds::default(),
force_tier: None,
pins,
};
assert!(!admits_pinned(&on, &hs, Tier::T3, Opt::Unfold));
assert!(admits_pinned(&on, &hs, Tier::T1, Opt::Fuse));
assert!(!admits_pinned(&on, &hs, Tier::T1, Opt::Specialize));
assert!(admits_pinned(&on, &hs, Tier::T2, Opt::Specialize));
assert!(admits_pinned(&on, &hs, Tier::T1, Opt::Inline));
assert!(!admits_pinned(&on, &hs, Tier::T1, Opt::Cse));
let off = OptimizationConfig::all_on().disable(Opt::Fuse);
assert!(!admits_pinned(&off, &hs, Tier::T3, Opt::Fuse));
}
#[test]
fn pin_spec_parses_keyword_value_pairs() {
let mut pins = PinSet::none();
apply_pin_spec(&mut pins, "specialize:eager, fuse:t1 ; unfold:never, bogus:t2, cse:nonsense");
assert_eq!(pins.get(Opt::Specialize), Some(Pin::Eager));
assert_eq!(pins.get(Opt::Fuse), Some(Pin::At(Tier::T1)));
assert_eq!(pins.get(Opt::Unfold), Some(Pin::Never));
assert_eq!(pins.get(Opt::Cse), None, "unparseable value is ignored");
}
#[test]
fn decorate_source_inserts_file_level_decorators() {
let src = "## To f (x: Int) -> Int:\n Return x.\n\n## Main\nShow 1.\n";
let d = decorate_source(src, &["scalarize", "unroll"]);
assert!(d.contains("## No scalarize\n## No unroll\n## Main"), "got:\n{d}");
assert!(d.starts_with("## To f"), "functions stay before the decorators");
assert_eq!(decorate_source(src, &[]), src);
let lib = "## To f (x: Int) -> Int:\n Return x.\n";
assert!(decorate_source(lib, &["cse"]).ends_with("## No cse\n"));
}
#[test]
fn decorate_tiers_inserts_file_level_pins() {
let src = "## To f (x: Int) -> Int:\n Return x.\n\n## Main\nShow 1.\n";
let d = decorate_tiers(src, &[("specialize", "eager"), ("unfold", "never")]);
assert!(
d.contains("## Tier specialize eager\n## Tier unfold never\n## Main"),
"got:\n{d}"
);
assert!(d.starts_with("## To f"), "functions stay before the decorators");
assert_eq!(decorate_tiers(src, &[]), src, "empty list is a no-op");
let lib = "## To f (x: Int) -> Int:\n Return x.\n";
assert!(decorate_tiers(lib, &[("fuse", "t1")]).ends_with("## Tier fuse t1\n"));
}
#[test]
fn by_keyword_round_trips() {
for m in REGISTRY {
assert_eq!(by_keyword(m.keyword), Some(m.opt));
assert_eq!(by_keyword(&m.keyword.to_uppercase()), Some(m.opt));
}
assert_eq!(by_keyword("nonsense"), None);
}
#[test]
fn all_on_enables_everything_all_off_nothing() {
let on = OptimizationConfig::all_on();
let off = OptimizationConfig::all_off();
for m in REGISTRY {
assert!(on.is_on(m.opt), "{:?} should be on", m.opt);
assert!(!off.is_on(m.opt), "{:?} should be off", m.opt);
}
assert!(off.is_all_off());
assert_eq!(OptimizationConfig::default(), on);
}
#[test]
fn serde_round_trips() {
let cfg = OptimizationConfig::all_on().disable(Opt::Scalarize).disable(Opt::Unroll);
let json = serde_json::to_string(&cfg).unwrap();
let back: OptimizationConfig = serde_json::from_str(&json).unwrap();
assert_eq!(cfg, back);
}
#[test]
fn normalize_disables_dependents_and_reports() {
let mut cfg = OptimizationConfig::all_on().disable(Opt::Cse);
let report = cfg.normalize();
assert!(!cfg.is_on(Opt::Scalarize), "Scalarize must follow Cse off");
assert!(report
.auto_disabled
.contains(&(Opt::Scalarize, Reason::DependencyOff(Opt::Cse))));
}
#[test]
fn normalize_oracle_closure() {
let mut cfg = OptimizationConfig::all_on().disable(Opt::Oracle);
cfg.normalize();
assert!(!cfg.is_on(Opt::ElemType));
assert!(!cfg.is_on(Opt::OracleHints));
assert!(!cfg.is_on(Opt::Unchecked));
}
#[test]
fn default_config_is_stable_under_normalize() {
let mut cfg = OptimizationConfig::all_on();
let report = cfg.normalize();
assert!(report.is_empty(), "default config must normalize to a no-op, got {:?}", report);
assert!(cfg.is_on(Opt::Interleave));
assert!(cfg.is_on(Opt::Unroll));
assert!(cfg.is_on(Opt::Scalarize));
assert_eq!(cfg, OptimizationConfig::all_on());
}
#[test]
fn safety_profile_drops_all_unsafe() {
let cfg = Profile::Safety.config();
for m in REGISTRY {
if m.emits_unsafe {
assert!(!cfg.is_on(m.opt), "Safety must disable unsafe-emitting {:?}", m.opt);
}
}
}
#[test]
fn memory_profile_drops_mem_hogs_keeps_savers() {
let cfg = Profile::Memory.config();
assert!(!cfg.is_on(Opt::Unroll));
assert!(!cfg.is_on(Opt::Scalarize));
assert!(cfg.is_on(Opt::Narrow) || !OptimizationConfig::all_on().is_on(Opt::Unbox));
assert!(cfg.is_on(Opt::Unbox), "Memory keeps memory-saving Unbox");
}
#[test]
fn from_toggles_reports_forced_off() {
let mut t = BTreeMap::new();
t.insert("cse".to_string(), false);
let (cfg, forced) = OptimizationConfig::from_toggles(&t);
assert!(!cfg.is_on(Opt::Cse));
assert!(!cfg.is_on(Opt::Scalarize));
assert!(forced.contains(&Opt::Scalarize));
}
#[test]
fn from_spec_master_off_is_all_off() {
assert!(OptimizationConfig::from_spec(true, None, None).is_all_off());
assert!(OptimizationConfig::from_spec(true, Some("safety"), Some("cse")).is_all_off());
}
#[test]
fn from_spec_default_is_all_on() {
assert_eq!(OptimizationConfig::from_spec(false, None, None), OptimizationConfig::all_on());
}
#[test]
fn from_spec_off_list_disables_listed_keywords() {
let cfg = OptimizationConfig::from_spec(false, None, Some("scalarize, unroll;nonsense"));
assert!(!cfg.is_on(Opt::Scalarize));
assert!(!cfg.is_on(Opt::Unroll));
assert!(cfg.is_on(Opt::Memo), "unlisted opts stay on; unknown tokens ignored");
}
#[test]
fn from_spec_off_list_normalizes_dependents() {
let cfg = OptimizationConfig::from_spec(false, None, Some("oracle"));
assert!(!cfg.is_on(Opt::Oracle));
assert!(!cfg.is_on(Opt::Unchecked));
assert!(!cfg.is_on(Opt::OracleHints));
assert!(!cfg.is_on(Opt::ElemType));
}
#[test]
fn from_spec_profiles_drop_the_right_opts() {
let safety = OptimizationConfig::from_spec(false, Some("safety"), None);
for m in REGISTRY {
if m.emits_unsafe {
assert!(!safety.is_on(m.opt), "Safety must drop unsafe-emitting {:?}", m.opt);
}
}
let memory = OptimizationConfig::from_spec(false, Some("memory"), None);
assert!(!memory.is_on(Opt::Unroll));
assert!(memory.is_on(Opt::Unbox), "Memory keeps memory-saving opts");
}
#[test]
fn disabling_any_requirement_cascades_to_dependents() {
for m in REGISTRY {
for &req in m.requires {
let mut cfg = OptimizationConfig::all_on();
cfg.set(req, false);
cfg.normalize();
assert!(
!cfg.is_on(m.opt),
"{:?} requires {:?}; disabling {:?} must cascade {:?} off",
m.opt, req, req, m.opt
);
}
}
}
#[test]
fn enable_with_requires_pulls_ancestors() {
let mut cfg = OptimizationConfig::all_off();
cfg.enable_with_requires(Opt::Interleave);
assert!(cfg.is_on(Opt::Interleave));
assert!(cfg.is_on(Opt::Scalarize), "Interleave needs Scalarize");
assert!(cfg.is_on(Opt::Cse), "…which needs Cse");
assert!(!cfg.is_on(Opt::Unroll), "unrelated opts stay off");
assert!(!cfg.is_on(Opt::Oracle), "unrelated opts stay off");
let report = cfg.normalize();
assert!(report.is_empty(), "an enabled chain must be normalize-stable: {report:?}");
}
fn role_of(tree: &[OptNode], opt: Opt) -> Option<OptRole> {
tree.iter().find(|n| n.opt == opt).map(|n| n.role)
}
fn depth_of(tree: &[OptNode], opt: Opt) -> usize {
tree.iter().find(|n| n.opt == opt).unwrap().depth
}
fn pos_of(tree: &[OptNode], opt: Opt) -> usize {
tree.iter().position(|n| n.opt == opt).unwrap()
}
#[test]
fn relationship_tree_pulls_enabler_parents_for_orphan_children() {
let fired = [Opt::Unchecked, Opt::OracleHints, Opt::ElemType];
let tree = relationship_tree(&fired, &[], &[]);
assert_eq!(role_of(&tree, Opt::Unchecked), Some(OptRole::Fired));
assert_eq!(role_of(&tree, Opt::Oracle), Some(OptRole::Enabler));
assert!(depth_of(&tree, Opt::Oracle) < depth_of(&tree, Opt::Unchecked));
assert!(tree.iter().find(|n| n.opt == Opt::Oracle).unwrap().has_children);
}
#[test]
fn relationship_tree_surfaces_preempted_losers() {
let fired = [Opt::DenseMap];
let preempted = [(Opt::DenseMap, Opt::NarrowMap)];
let tree = relationship_tree(&fired, &preempted, &[]);
let node = tree.iter().find(|n| n.opt == Opt::NarrowMap).expect("narrowmap present");
assert_eq!(node.role, OptRole::Preempted);
assert!(node.preempted_by.contains(&Opt::DenseMap));
assert_eq!(role_of(&tree, Opt::DenseMap), Some(OptRole::Fired));
}
#[test]
fn relationship_tree_nests_requires_chain_by_depth() {
let fired = [Opt::Cse, Opt::Scalarize, Opt::Interleave];
let tree = relationship_tree(&fired, &[], &[]);
assert_eq!(depth_of(&tree, Opt::Cse), 0);
assert_eq!(depth_of(&tree, Opt::Scalarize), 1);
assert_eq!(depth_of(&tree, Opt::Interleave), 2);
assert!(pos_of(&tree, Opt::Cse) < pos_of(&tree, Opt::Scalarize));
assert!(pos_of(&tree, Opt::Scalarize) < pos_of(&tree, Opt::Interleave));
}
#[test]
fn relationship_tree_is_deterministic_and_only_in_play() {
let fired = [Opt::DenseMap, Opt::Unchecked];
let preempted = [(Opt::DenseMap, Opt::NarrowMap)];
let a = relationship_tree(&fired, &preempted, &[]);
let b = relationship_tree(&fired, &preempted, &[]);
let keys = |t: &[OptNode]| {
t.iter().map(|n| (n.opt, n.depth, n.role)).collect::<Vec<_>>()
};
assert_eq!(keys(&a), keys(&b), "derivation must be deterministic");
let in_play: std::collections::BTreeSet<Opt> = a.iter().map(|n| n.opt).collect();
let expected: std::collections::BTreeSet<Opt> =
[Opt::DenseMap, Opt::NarrowMap, Opt::Unchecked, Opt::Oracle].into_iter().collect();
assert_eq!(in_play, expected, "only in-play opts appear");
}
#[test]
fn relationship_tree_nests_by_per_program_dependencies() {
let fired = [Opt::Scalarize, Opt::DeadCode, Opt::Cse];
let deps = [(Opt::DeadCode, Opt::Scalarize)];
let tree = relationship_tree(&fired, &[], &deps);
assert!(depth_of(&tree, Opt::DeadCode) > depth_of(&tree, Opt::Scalarize));
assert!(pos_of(&tree, Opt::Scalarize) < pos_of(&tree, Opt::DeadCode));
let dc = tree.iter().find(|n| n.opt == Opt::DeadCode).unwrap();
assert!(dc.depends_on.contains(&Opt::Scalarize));
assert_eq!(dc.role, OptRole::Fired);
assert!(tree.iter().find(|n| n.opt == Opt::Scalarize).unwrap().has_children);
assert!(depth_of(&tree, Opt::Scalarize) > depth_of(&tree, Opt::Cse));
}
}