Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
8125361
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 17, 2026
a03d920
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 17, 2026
5e58373
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 17, 2026
4a030d4
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 17, 2026
c8e4437
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 17, 2026
f3ac0a5
fix(infer): shard target_model VEP decode; confirm H2a; add perf witn…
briansrls Jun 17, 2026
701140d
merge origin/session/tidy-fox-905; keep perf-fix infer + test harness
briansrls Jun 17, 2026
d827fea
fix(stage0): restore gunbc ci subcommand dropped by regen
briansrls Jun 17, 2026
cf6459f
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 17, 2026
c0cb6e7
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 17, 2026
d6ca17a
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 17, 2026
b2846f9
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 17, 2026
dd7e9fc
WIP: Resolve/infer perf fix: 03_body_producer.dag 244s super-linear i…
briansrls Jun 18, 2026
29d68d5
merge origin/main into fix/infer-perf-body-producer
briansrls Jun 18, 2026
b63934d
fix(complexity_gate): use outcome_accepted for infer-perf subject pro…
briansrls Jun 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions src/v1/04_infer.dag
Original file line number Diff line number Diff line change
Expand Up @@ -5475,8 +5475,13 @@ fn substitute_generics(n: Node, subst: Map<String, Node>, source_indices: Map<St
// which would duplicate a type's canonical declaration in the nodes table and
// change identity for downstream consumers. Returning n unchanged keeps every
// non-generic call (and any generic call that bound nothing) a true no-op.
if (map_keys(subst) |> count) == 0 { n }
else {
// Empty check stays at this entry only — subst is unchanged through recursion,
// so map_keys must not run per tree node (was super-linear on body_producer infer).
if map_is_empty(subst) { n }
else { substitute_generics_apply(n: n, subst: subst, source_indices: source_indices) }
}

fn substitute_generics_apply(n: Node, subst: Map<String, Node>, source_indices: Map<String, NewlineIndex>) -> Node {
match n.inferred {
Present { value: TypeVariable { id: tv_id } } =>
match map_get(subst, tv_id) {
Expand All @@ -5494,11 +5499,11 @@ fn substitute_generics(n: Node, subst: Map<String, Node>, source_indices: Map<St
} else {
Node {
name: n.name, span: n.span, ident_span: n.ident_span,
children: n.children |> map(c => substitute_generics(n: c, subst: subst, source_indices: source_indices)),
children: n.children |> map(c => substitute_generics_apply(n: c, subst: subst, source_indices: source_indices)),
connective: n.connective,
params: n.params |> map(p => substitute_generics(n: p, subst: subst, source_indices: source_indices)),
params: n.params |> map(p => substitute_generics_apply(n: p, subst: subst, source_indices: source_indices)),
inferred: match n.inferred {
Present { value: Resolved { node: rt } } => Present { value: Resolved { node: substitute_generics(n: rt, subst: subst, source_indices: source_indices) } }
Present { value: Resolved { node: rt } } => Present { value: Resolved { node: substitute_generics_apply(n: rt, subst: subst, source_indices: source_indices) } }
_ => n.inferred
},
return_cardinality: n.return_cardinality, uses: n.uses, body: n.body, transport: n.transport,
Expand All @@ -5508,7 +5513,6 @@ fn substitute_generics(n: Node, subst: Map<String, Node>, source_indices: Map<St
}
}
}
}
}

// Merge type bindings from parent environments, filtering out algebra
Expand Down
4 changes: 4 additions & 0 deletions src/v1/04_method.dag
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ fn builtin_function_registry() -> Map<String, Node> {
let m = map_insert(m, "list_push", list_of_type_variable(id: "collection_element"))
let m = map_insert(m, "hash_combine", hash_type)
let m = map_insert(m, "atom_identity_hash", hash_type)
let m = map_insert(m, "string_contains", bool_type)
let m = map_insert(m, "filesystem_read", type_variable_node(id: "filesystem_read_result"))
let m = map_insert(m, "extdeps_dead_param_count_for_operation", int_type)
let m = map_insert(m, "extdeps_dead_param_count_for_path", int_type)
m
}

Expand Down
4 changes: 4 additions & 0 deletions src/v1/runtime_rust.dag
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ fn rt_collection_ops() -> String {
" m.get(&key).cloned()\n",
"}\n\n",
"pub fn map_keys<K: Clone, V>(m: &HashMap<K, V>) -> Vec<K> { m.keys().cloned().collect() }\n\n",
"pub fn map_is_empty<K, V>(m: &HashMap<K, V>) -> bool { m.is_empty() }\n\n",
"pub fn rc_vec_ptr_eq<T>(a: &Rc<Vec<Rc<T>>>, b: &Rc<Vec<Rc<T>>>) -> bool {\n",
" a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| Rc::ptr_eq(x, y))\n",
"}\n\n",
"pub fn map_values<K, V: Clone>(m: &HashMap<K, V>) -> Vec<V> { m.values().cloned().collect() }\n\n",
"pub fn list_concat<T>(mut a: Vec<T>, b: Vec<T>) -> Vec<T> { a.extend(b); a }\n\n",
"pub fn list_push<T>(mut list: Vec<T>, item: T) -> Vec<T> { list.push(item); list }\n\n",
Expand Down
2 changes: 2 additions & 0 deletions src/v1/stage0/src/bin/claim_batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,7 @@ fn resolve_timed(
timings: &mut ResolveTimings,
) -> Result<ResolvedEntry, ExitCode> {
let started = Instant::now();
v1_compiler::generic_instantiation_instrument::reset();
match resolve_entry_with_index(index, entry) {
Ok((graph, source_indices)) => {
let ms = started.elapsed().as_millis();
Expand All @@ -566,6 +567,7 @@ fn resolve_timed(
graph.modules.len(),
graph.item_registry.len(),
);
v1_compiler::generic_instantiation_instrument::eprint_report(entry);
timings.resolves += 1;
timings.resolve_ms += ms;
Ok((graph, source_indices))
Expand Down
8 changes: 4 additions & 4 deletions src/v1/stage0/src/bin/discover_source_root_ingest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ use std::path::PathBuf;
use std::process::ExitCode;

use v1_compiler::cli_run::{
discover_source_root_reads, emit_source_root_ingest_manifest, source_root_ingest_content_hash_fnv1a64,
discover_source_root_reads, emit_source_root_ingest_manifest,
source_root_ingest_content_hash_fnv1a64,
};

fn require_value(args: &[String], idx: usize, flag: &str) -> Result<String, ExitCode> {
Expand All @@ -34,9 +35,8 @@ fn run() -> Result<ExitCode, ExitCode> {
let args: Vec<String> = std::env::args().collect();
let mut source_roots: Vec<String> = Vec::new();
let mut scan_dir = "src/v2/test/fixture/program_assembly".to_string();
let mut exclude_subpaths: Vec<String> = vec![
"host_source_root_ingest_manifest.dag".to_string(),
];
let mut exclude_subpaths: Vec<String> =
vec!["host_source_root_ingest_manifest.dag".to_string()];
let mut manifest_path: Option<PathBuf> = None;

let mut i = 1;
Expand Down
6 changes: 2 additions & 4 deletions src/v1/stage0/src/cli_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2018,10 +2018,8 @@ fn emit_source_root_read_witness(rec: &SourceRootReadRecord) -> String {
}

fn emit_source_root_ingest_monoid(records: &[SourceRootReadRecord]) -> String {
let mut witness_nodes: Vec<String> = records
.iter()
.map(emit_source_root_read_witness)
.collect();
let mut witness_nodes: Vec<String> =
records.iter().map(emit_source_root_read_witness).collect();
let mut out = String::from("Empty");
while let Some(head) = witness_nodes.pop() {
out = format!("Cons {{\n head: {head},\n tail: {out}\n}}");
Expand Down
140 changes: 140 additions & 0 deletions src/v1/stage0/src/generic_instantiation_instrument.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Hand-maintained instrumentation for H2a/H2b confirmation (generic instantiation counters).
// Enable with GUNBC_INSTRUMENT_GENERIC_INSTANTIATION=1; report printed by claim_batch after resolve.

use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};

use crate::v1_std_core::Node;

type SiteKey = u64;

thread_local! {
static SUBST_CALLS: RefCell<u64> = const { RefCell::new(0) };
static UNIFY_CALLS: RefCell<u64> = const { RefCell::new(0) };
static SUBST_SITE_KEYS: RefCell<HashMap<SiteKey, u64>> = RefCell::new(HashMap::new());
static ENABLED_FLAG: RefCell<bool> = const { RefCell::new(false) };
}

static ENV_CHECKED: AtomicBool = AtomicBool::new(false);

fn hash_str(s: &str) -> SiteKey {
let mut h: SiteKey = 0xcbf29ce484222325;
for b in s.bytes() {
h ^= b as SiteKey;
h = h.wrapping_mul(0x100000001b3);
}
h
}

fn combine_hash(a: SiteKey, b: SiteKey) -> SiteKey {
a.wrapping_mul(0x100000001b3) ^ b
}

fn ensure_env_checked() {
if ENV_CHECKED.swap(true, Ordering::Relaxed) {
return;
}
if std::env::var("GUNBC_INSTRUMENT_GENERIC_INSTANTIATION")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
{
ENABLED_FLAG.with(|f| *f.borrow_mut() = true);
}
}

pub fn enabled() -> bool {
ensure_env_checked();
ENABLED_FLAG.with(|f| *f.borrow())
}

pub fn reset() {
SUBST_CALLS.with(|c| *c.borrow_mut() = 0);
UNIFY_CALLS.with(|c| *c.borrow_mut() = 0);
SUBST_SITE_KEYS.with(|m| m.borrow_mut().clear());
}

fn node_shape_fingerprint(n: &Rc<Node>) -> SiteKey {
let label = if !n.name.is_empty() {
n.name.clone()
} else {
format!("{:?}", n.connective)
};
let child_count = n.children.len() as SiteKey;
let param_count = n.params.len() as SiteKey;
combine_hash(hash_str(&label), combine_hash(child_count, param_count))
}

fn subst_fingerprint(subst_len: usize) -> SiteKey {
subst_len as SiteKey
}

pub fn record_substitute_generics(formal: &Rc<Node>, subst_len: usize) {
if !enabled() {
return;
}
SUBST_CALLS.with(|c| *c.borrow_mut() += 1);
let site_key = combine_hash(node_shape_fingerprint(formal), subst_fingerprint(subst_len));
SUBST_SITE_KEYS.with(|m| {
*m.borrow_mut().entry(site_key).or_insert(0) += 1;
});
}

pub fn record_unify_generics() {
if !enabled() {
return;
}
UNIFY_CALLS.with(|c| *c.borrow_mut() += 1);
}

pub struct GenericInstantiationReport {
pub subst_calls: u64,
pub unify_calls: u64,
pub distinct_subst_sites: u64,
pub max_site_redundancy: u64,
pub total_redundant_subst_calls: u64,
}

pub fn snapshot_report() -> GenericInstantiationReport {
let subst_calls = SUBST_CALLS.with(|c| *c.borrow());
let unify_calls = UNIFY_CALLS.with(|c| *c.borrow());
let (distinct_subst_sites, max_site_redundancy, total_redundant_subst_calls) =
SUBST_SITE_KEYS.with(|m| {
let map = m.borrow();
let distinct = map.len() as u64;
let max_red = map.values().copied().max().unwrap_or(0);
let redundant = map.values().map(|&c| c.saturating_sub(1)).sum::<u64>();
(distinct, max_red, redundant)
});
GenericInstantiationReport {
subst_calls,
unify_calls,
distinct_subst_sites,
max_site_redundancy,
total_redundant_subst_calls,
}
}

pub fn eprint_report(context: &str) {
if !enabled() {
return;
}
let r = snapshot_report();
eprintln!("[generic-inst] {context}");
eprintln!(
" substitute_generics calls: {} distinct site keys: {} max repeats/site: {} redundant calls (count-1 per site): {}",
r.subst_calls,
r.distinct_subst_sites,
r.max_site_redundancy,
r.total_redundant_subst_calls
);
eprintln!(" unify_generics calls: {}", r.unify_calls);
if r.distinct_subst_sites > 0 {
let ratio = r.subst_calls as f64 / r.distinct_subst_sites as f64;
eprintln!(
" calls/distinct-site ratio: {:.1} (H2a favored if ratio >> 4 and redundant >> distinct)",
ratio
);
}
}
146 changes: 146 additions & 0 deletions src/v1/stage0/src/interface_elaboration_instrument.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// Hand-maintained instrumentation for interface-elaboration localization (merry-crab brief).
// Enable with GUNBC_INSTRUMENT_INTERFACE_ELABORATION=1.

use std::cell::RefCell;
use std::collections::HashMap;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};

#[derive(Default, Clone)]
struct ImportMergeStats {
calls: u64,
total: Duration,
max: Duration,
}

thread_local! {
static ENABLED: RefCell<bool> = const { RefCell::new(false) };
static TYPECHECK_TOTAL: RefCell<HashMap<String, Duration>> = RefCell::new(HashMap::new());
static BUILD_ENV_TOTAL: RefCell<HashMap<String, Duration>> = RefCell::new(HashMap::new());
static IMPORT_MERGE: RefCell<HashMap<String, ImportMergeStats>> = RefCell::new(HashMap::new());
static MERGE_SCOPE_TOTAL: RefCell<HashMap<String, Duration>> = RefCell::new(HashMap::new());
}

static ENV_CHECKED: AtomicBool = AtomicBool::new(false);

fn ensure_env_checked() {
if ENV_CHECKED.swap(true, Ordering::Relaxed) {
return;
}
if std::env::var("GUNBC_INSTRUMENT_INTERFACE_ELABORATION")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
{
ENABLED.with(|f| *f.borrow_mut() = true);
}
}

pub fn enabled() -> bool {
ensure_env_checked();
ENABLED.with(|f| *f.borrow())
}

pub fn reset() {
TYPECHECK_TOTAL.with(|m| m.borrow_mut().clear());
BUILD_ENV_TOTAL.with(|m| m.borrow_mut().clear());
IMPORT_MERGE.with(|m| m.borrow_mut().clear());
MERGE_SCOPE_TOTAL.with(|m| m.borrow_mut().clear());
}

pub struct ElabGuard {
map: &'static std::thread::LocalKey<RefCell<HashMap<String, Duration>>>,
key: String,
start: Instant,
}

impl ElabGuard {
pub fn new(
map: &'static std::thread::LocalKey<RefCell<HashMap<String, Duration>>>,
key: String,
) -> Option<Self> {
if !enabled() {
return None;
}
Some(Self {
map,
key,
start: Instant::now(),
})
}
}

impl Drop for ElabGuard {
fn drop(&mut self) {
let elapsed = self.start.elapsed();
self.map.with(|m| {
*m.borrow_mut().entry(self.key.clone()).or_insert(Duration::ZERO) += elapsed;
});
}
}

pub fn record_import_merge(consumer: &str, import_path: &str, elapsed: Duration) {
if !enabled() {
return;
}
let key = format!("{consumer} <- {import_path}");
IMPORT_MERGE.with(|m| {
let mut binding = m.borrow_mut();
let entry = binding.entry(key).or_default();
entry.calls += 1;
entry.total += elapsed;
if elapsed > entry.max {
entry.max = elapsed;
}
});
}

pub fn eprint_report(context: &str) {
if !enabled() {
return;
}
eprintln!("[interface-elab] {context}");

fn print_duration_map(label: &str, map: &HashMap<String, Duration>) {
if map.is_empty() {
return;
}
let mut rows: Vec<_> = map.iter().collect();
rows.sort_by(|a, b| b.1.cmp(a.1));
eprintln!(" {label}:");
for (name, dur) in rows.iter().take(20) {
eprintln!(" {name}: {dur:?}");
}
}

TYPECHECK_TOTAL.with(|m| print_duration_map("typecheck_module", &m.borrow()));
BUILD_ENV_TOTAL.with(|m| print_duration_map("build_type_env", &m.borrow()));
MERGE_SCOPE_TOTAL.with(|m| print_duration_map("merge_scope_from_imports", &m.borrow()));

IMPORT_MERGE.with(|m| {
let map = m.borrow();
if map.is_empty() {
return;
}
let mut rows: Vec<_> = map.iter().collect();
rows.sort_by(|a, b| b.1.total.cmp(&a.1.total));
eprintln!(" build_type_env import_bindings merge:");
for (key, stats) in rows.iter().take(20) {
eprintln!(
" {key}: total={:?} calls={} max={:?}",
stats.total, stats.calls, stats.max
);
}
});
}

pub fn typecheck_guard(module_name: &str) -> Option<ElabGuard> {
ElabGuard::new(&TYPECHECK_TOTAL, module_name.to_string())
}

pub fn build_env_guard(module_name: &str) -> Option<ElabGuard> {
ElabGuard::new(&BUILD_ENV_TOTAL, module_name.to_string())
}

pub fn merge_scope_guard(consumer: &str) -> Option<ElabGuard> {
ElabGuard::new(&MERGE_SCOPE_TOTAL, consumer.to_string())
}
Loading
Loading