LLVM optimization often (always?) results of loss of closure upvar debug info
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
I'm filing this here for tracking even though this is largely/entirely an LLVM issue (llvm/llvm-project#141559, NB: the title of that issue is not accurate at the time of filing this one)
use std::{env, process};
use std::str::FromStr;
fn main() {
let needle = env::var("NEEDLE").unwrap();
let ret = i64::from_str(&env::var("RETURN").unwrap()).unwrap();
let ret2 = i64::from_str(&env::var("RETURN2").unwrap()).unwrap();
let check = |foobar| {
if foobar == needle {
process::exit(ret as i32 - ret2 as i32);
return true;
}
false
};
for s in env::args() {
if check(s) {
unreachable!();
}
}
}
This program has been reduced from iterator heavy code but intentionally made weird in certain ways to defeat the optimizer (getting upvar values from env vars), highlight the issue with the closure (eschewing the use of the Iterator trait), and to force the rustc-generated type capturing the upvars to be of sufficient size.
rustc's MIR optimizations do not inline the closure check, leaving that for LLVM to do. Once LLVM inlines it, SROA then sets to work on the upvar-capturing-type and splits it into its components. LLVM does correctly move the debug value definitions for the upvars within the closure to the points at which the equivalent variables in the outer function are defined, and it does correctly rewrite the DWARF expressions to "$rsp plus offset plus deref".
Where LLVM falls down is that the LiveDebugValues pass does not propagate the value definition through basic blocks where the corresponding variable is not in scope (unless the entire block is "artificial"). This essentially guarantees that the value will not be propagated through to the closure and will be lost.
There is some discussion in the linked LLVM issue of how to fix this and what the performance costs might be.
@rustbot label +A-llvm +A-debuginfo +WG-llvm
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the reduced Rust program in the issue and the linked LLVM issue llvm/llvm-project#141559. Investigate the interaction between LLVM inlining, SROA, and the LiveDebugValues pass, focusing on why closure upvar debug values are not propagated through out-of-scope blocks. Done means the closure's upvar debug information is preserved after optimization.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100