Nothing frees a `memref.alloc`: every buffer the compiler allocates is an unmatched `malloc`
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 14
- Forks
- 2
- Avg merge
- 12h 42m
- Merged PRs (30d)
- 61
Description
The string dealloc does not appear anywhere in src/. No emitter writes a
memref.dealloc, and neither pass pipeline -- get_optimization_pipeline
(src/driver.rs:1389) or the copy in src/codegen/mod.rs:134 -- runs a buffer-deallocation
pass. Every memref.alloc the compiler emits therefore becomes a malloc that is never
freed.
There are seven sites that emit one:
- src/codegen/flat/emit/tensor.rs:90 (
TensorAlloc: array literals andTensor<T>(...)) - src/codegen/flat/emit/tensor.rs:205 (transpose result)
- src/codegen/flat/emit/tensor.rs:267 (
TensorMapresult) - src/codegen/flat/emit/tensor.rs:883
- src/codegen/flat/emit/control.rs:100
- src/codegen/flat/emit/arith.rs:164 (elementwise
linalgresult) - src/codegen/generator.rs:317 (the AST path)
This leaks per call, not per program:
fn sum_one() -> i32 {
let l = [1, 2, 3, 4];
return l[0] + l[3];
}
fn main() -> i32 {
let mut total = 0;
for i in 0..1000000 {
total = total + sum_one();
}
return total % 7;
}
sum_one compiles to %1 = call ptr @malloc(i64 16) with no matching free. Linked at
-O0 -- which is what --run uses -- the binary peaks at 32.6 MB RSS for a program
whose live data is one i32. It answers correctly; it just keeps everything.
Two halves, and they want different answers.
The buffers that are plainly frame-local should never have been heap in the first place:
that is #641, and it removes most of the traffic rather than balancing it.
What remains is genuinely dynamic -- a ?-shaped Tensor<T>(n), an elementwise or
map result whose extent is read off an operand -- and those need an owner. Worth
deciding deliberately rather than reaching for MLIR's buffer-deallocation: the ownership
question is a language question here, and #495 (Vec has no drop, TYPE_NEEDS_DROP
reserved and never set) and #340 (a device transfer's free is emitted at end of block
rather than from a lifetime) are asking the same one from two other directions. A buffer
whose lifetime the frontend already knows should get its free from that lifetime, not from
a late pass re-deriving it.
Ordering note: this is the half that cannot be fixed by lowering alone, so it is the one
that should shape the model. #641 is worth landing first regardless, because it is
correct on its own terms and shrinks the problem this issue has to solve.
Contributor guide
No contributing guide indexed for this repository
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 seven allocation sites in src/codegen/flat/emit/tensor.rs, src/codegen/flat/emit/control.rs, src/codegen/flat/emit/arith.rs, and src/codegen/generator.rs, then read the pipelines in src/driver.rs:1389 and src/codegen/mod.rs:134. Compare the ownership questions with #641, #495, and #340 before choosing a model for dynamic buffers. Done means dynamically sized or derived buffers have an explicit frontend-known owner and are freed according to that lifetime, without relying on late buffer deallocation.
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
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100