Skip to content

Commit 15601b2

Browse files
olwangclaude
andcommitted
feat(jit-report): attribute cost-model declines to the actual function at runtime (item #2)
The report's per-function "declined by cost model" verdict re-derived the profitability from a bare re-translation, which loses profile-guided PICs (no profile feedback at report time) — so the very case that most often stays interpreted was not attributed. Now `consult_profitability` takes the function name and records it in a new `unprofitable_declined_fns` map (fn name → first decline reason) as the run happens — ground truth. The report reads that map for the per-function verdict instead of re-deriving, so a profile-guided PIC decline is now correctly attributed to its function. `report` mode's per-region log line also gains the `fn=...` tag, and the map is exposed in `to_json`. Test: report_explains_cost_model_decline_for_polymorphic_pic now additionally asserts the per-function `not native: declined by cost model` line (previously only the summary block was reliable). Verified: clippy 0; lib 276/0; runtime 453/0 (incl. all 11 report tests); differential 33/0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent fa5b94f commit 15601b2

3 files changed

Lines changed: 48 additions & 23 deletions

File tree

crates/rsscript/src/reg_vm/mod.rs

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2434,6 +2434,11 @@ pub struct NativeStats {
24342434
/// profitability is a distinct, post-eligibility judgement and must not be
24352435
/// clobbered by it.
24362436
pub unprofitable_decline_reasons: BTreeMap<String, u64>,
2437+
/// Runtime attribution: for each function the cost model declined this run, the
2438+
/// (first) decline reason — ground truth for the report's per-function "declined
2439+
/// by cost model" verdict, so it need not re-derive (which loses profile-guided
2440+
/// PICs). Keyed by function name.
2441+
pub unprofitable_declined_fns: BTreeMap<String, String>,
24372442
}
24382443

24392444
#[cfg(feature = "native-jit")]
@@ -2551,6 +2556,7 @@ compile_ms={:.3} run_ms={:.3} osr_entries={} unprofitable_declines={}",
25512556
"osr_entries": self.osr_entries,
25522557
"unprofitable_declines": self.unprofitable_declines,
25532558
"unprofitable_decline_reasons": &self.unprofitable_decline_reasons,
2559+
"unprofitable_declined_fns": &self.unprofitable_declined_fns,
25542560
})
25552561
}
25562562
}
@@ -2589,24 +2595,23 @@ fn jit_missed_opt_report(unit: &RegUnit, native: &NativeState) -> Vec<String> {
25892595

25902596
// --- Native-tier verdict --------------------------------------------------
25912597
match translate_to_native_jit(unit, func) {
2592-
Some((jit_fn, ..)) => {
2598+
Some(_) => {
25932599
if native.report_native_ok.contains(&key) {
25942600
block.push(" native: ok".to_string());
2601+
} else if let Some(reason) =
2602+
native.stats.unprofitable_declined_fns.get(&func.name)
2603+
{
2604+
// Runtime attribution (ground truth from this run's cost-model
2605+
// consult) — the common "why no JIT" case now the model enforces
2606+
// by default. Reliable even for profile-guided PICs, which a
2607+
// re-derivation here would miss.
2608+
block.push(format!(" not native: declined by cost model — {reason}"));
25952609
} else {
2596-
// Eligible but never observed running natively this run. Distinguish
2597-
// the cost-model (profitability) decline — the common "why no JIT"
2598-
// case now that the model enforces by default — from a plain
2599-
// tier-deferred/not-hot miss.
2600-
let profit = effective_cost_mode().active().then(|| {
2601-
native_region_profitability(&jit_fn, jit_function_has_loop(&func.code))
2602-
});
2603-
match profit {
2604-
Some(p) if p.decline => block
2605-
.push(format!(" not native: declined by cost model — {}", p.reason(&func.name))),
2606-
_ => block.push(
2607-
" native: eligible (not run natively this execution)".to_string(),
2608-
),
2609-
}
2610+
// Eligible but never observed running natively this run
2611+
// (tier-deferred, not called hot, or demoted by another gate).
2612+
block.push(
2613+
" native: eligible (not run natively this execution)".to_string(),
2614+
);
26102615
}
26112616
}
26122617
None => {

crates/rsscript/src/reg_vm/tier.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ fn consult_profitability(
162162
jit_fn: &vm_jit::JitFunction,
163163
has_backedge: bool,
164164
region: &str,
165+
func_name: &str,
165166
) -> bool {
166167
let mode = effective_cost_mode();
167168
if !mode.active() {
@@ -170,7 +171,7 @@ fn consult_profitability(
170171
let p = native_region_profitability(jit_fn, has_backedge);
171172
if matches!(mode, CostMode::Report) && std::env::var_os("RSS_JIT_REPORT").is_some() {
172173
// Log EVERY scored region (kept and declined) so weights can be calibrated.
173-
eprintln!("[cost-model] {}", p.summary(region));
174+
eprintln!("[cost-model] {} fn=`{func_name}`", p.summary(region));
174175
}
175176
if !p.decline {
176177
return false;
@@ -182,6 +183,14 @@ fn consult_profitability(
182183
.unprofitable_decline_reasons
183184
.entry(p.reason(region))
184185
.or_insert(0) += 1;
186+
// Runtime ATTRIBUTION: record which actual function/region was declined, so
187+
// the report can say per-function "declined by cost model" from ground truth
188+
// rather than a fragile re-derivation (which loses profile-guided PICs).
189+
native
190+
.stats
191+
.unprofitable_declined_fns
192+
.entry(func_name.to_string())
193+
.or_insert_with(|| p.reason(region));
185194
}
186195
// `report` observes but never changes execution; only `enforce` declines.
187196
matches!(mode, CostMode::Enforce)
@@ -996,7 +1005,13 @@ impl RegVm {
9961005
// function on the interpreter (cached below as not-native). `off`
9971006
// and `report` modes never change execution here.
9981007
let has_backedge = jit_function_has_loop(&func.code);
999-
if consult_profitability(native, &jit_fn, has_backedge, "whole-fn") {
1008+
if consult_profitability(
1009+
native,
1010+
&jit_fn,
1011+
has_backedge,
1012+
"whole-fn",
1013+
&func.name,
1014+
) {
10001015
None
10011016
} else {
10021017
let started = native.collect_stats.then(std::time::Instant::now);
@@ -1756,7 +1771,7 @@ impl RegVm {
17561771
// Step 1 cost model: an OSR loop is always a back-edge region;
17571772
// in `enforce` mode decline an unprofitable loop and resume on
17581773
// the interpreter (correctness-safe).
1759-
if consult_profitability(native, &jit_fn, true, "osr") {
1774+
if consult_profitability(native, &jit_fn, true, "osr", &func.name) {
17601775
return None;
17611776
}
17621777
let heap_input_regs = osr_heap_input_regs(&jit_fn);
@@ -2140,7 +2155,7 @@ impl RegVm {
21402155
// Step 1 cost model: an OSR loop is always a back-edge
21412156
// region; in `enforce` mode decline an unprofitable loop
21422157
// and resume on the interpreter (correctness-safe).
2143-
if consult_profitability(native, &jit_fn, true, "osr") {
2158+
if consult_profitability(native, &jit_fn, true, "osr", &func.name) {
21442159
return None;
21452160
}
21462161
let heap_input_regs = osr_heap_input_regs(&jit_fn);

crates/rsscript/tests/jit_acceptance.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4574,15 +4574,20 @@ fn main() -> Unit {
45744574
"the PIC must be declined under the default cost model: {stats:?}",
45754575
);
45764576
let report = lines.join("\n");
4577-
// The cost-model decline summary is built from the actual run's telemetry, so it
4578-
// reliably shows the PIC decline with its score breakdown. (The per-function
4579-
// verdict re-translates WITHOUT profile feedback, so it may not reproduce the
4580-
// profile-guided PIC — the summary block is the dependable signal here.)
4577+
// The cost-model decline summary (built from this run's telemetry) shows the PIC
4578+
// decline with its score breakdown...
45814579
assert!(
45824580
report.contains("jit-report: cost-model decline summary")
45834581
&& report.contains("pic_sites=1"),
45844582
"report must summarize the cost-model decline with its score breakdown, got:\n{report}",
45854583
);
4584+
// ...and the per-function verdict now attributes it to the actual declined
4585+
// function via runtime attribution (item #2) — reliable even for a profile-guided
4586+
// PIC, which a re-derivation would miss.
4587+
assert!(
4588+
report.contains("not native: declined by cost model"),
4589+
"a function block must be attributed to the cost-model decline, got:\n{report}",
4590+
);
45864591
}
45874592

45884593
#[cfg(feature = "native-jit")]

0 commit comments

Comments
 (0)