Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions changelog.d/8072-inprocess-scratch-dir-leak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
### Fixed

- **Successful in-process LLVM statepoint compiles no longer leave an empty
scratch directory behind.** Cleanup now removes the per-compile directory as
a unit, matching the clang path and preventing temporary-directory growth
proportional to the number of compiles. A focused regression explicitly
selects native-root lowering and verifies that repeated compiles leave no
scratch entries.
10 changes: 8 additions & 2 deletions crates/perry-codegen/src/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -876,8 +876,14 @@ fn compile_ll_inprocess_in(
let obj = fs::read(&plan.obj_path)
.with_context(|| format!("Failed to read assembled {}", plan.obj_path.display()))?;
if !policy.keep {
let _ = fs::remove_file(asm_path);
let _ = fs::remove_file(&plan.obj_path);
// `remove_dir_all`, not the two names we know about — the same
// reason the clang path gives. This arm is the only in-process
// one that CREATES the scratch dir (writing the assembly), so
// unlinking just the files left an empty husk per compile: a
// leak counted in compiles rather than in distinct IR, which is
// what turned the temp-hygiene gate red once this backend
// became the default and the clang cleanup stopped running.
let _ = fs::remove_dir_all(&paths.scratch_dir);
}
Ok(obj)
}
Expand Down
32 changes: 32 additions & 0 deletions crates/perry-codegen/src/linker_temp_lifecycle_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,35 @@ fn elf_compile_with(
}
fs::read(&obj).ok()
}

#[test]
fn inprocess_statepoint_compile_leaves_no_empty_scratch_dir() {
// The leak that turned `repsel-census` red on main for eight consecutive
// nightlies: 58 compiles left 58 `perry_llvm_scratch_<pid>_<counter>`
// directories, one per compile.
//
// Why the tests above did not see it. They drive `compile_ll_to_object_in`,
// which takes the CLANG path, and clang's cleanup has always been a
// `remove_dir_all`. The in-process backend removed the two files it knew
// about and left the directory — harmless while clang was the default, a
// leak per compile once in-process became it.
//
// The explicit native-roots decision is what makes this test non-vacuous.
// Only the statepoint backends route through assembly, and only that arm
// CREATES the scratch dir; `false` would exercise a path that never creates
// the directory this regression is meant to observe.
let Some(root) = temp_root_if_clang_available("inprocess_statepoint") else {
return;
};
for nth in 0..3 {
let bytes = compile_ll_inprocess_in(&root, &test_ir(100 + nth), None, CLEAN, true)
.unwrap_or_else(|e| panic!("in-process compile {nth} failed: {e:#}"));
assert!(!bytes.is_empty(), "compile {nth} produced no object bytes");
assert_eq!(
entries(&root),
Vec::<String>::new(),
"in-process compile {nth} left a scratch directory behind"
);
}
let _ = fs::remove_dir_all(&root);
}
Loading