[llvm-dev] [RFC] Adding support for marking allocator functions in LLVM IR

Jessica Clarke via llvm-dev
Thu, 06 Jan 2022 00:58:09 -0000

On 5 Jan 2022, at 22:32, Augie Fackler via llvm-dev <llvm-dev at
lists.llvm.org> wrote:
>
> Hi everyone! I’m working on making the Rust compiler being able to track
LLVM HEAD more closely, and as part of that we need to obviate a patch[0] that
teaches LLVM about some Rust allocator implementation details. This proposal is
the product of many conversations and a couple of failed attempts at simpler
implementations.
>
> Background
> =======> Rust uses LLVM for codegen, and has its own allocator
functions. In order for LLVM to correctly optimize out allocations we have to
tell the optimizer about the allocation/deallocation functions used by Rust.
>
> Languages supported by Clang, such as C and C++, have stable symbol names
for their allocation functions, which are hardcoded in LLVM[1][2].
Unfortunately, this strategy does not work for Rust, where developers don't
want to commit to a particular symbol name and calling convention yet.
>
> Proposal
> ======> We add two attributes to LLVM IR:
>
>  * `allocator(FAMILY)`: Marks a function as part of an allocator family,
named by the “primary” allocation function (e.g. `allocator(“malloc”)`,
`allocator(“_Znwm”)`, or `allocator(“__rust_alloc”)`).

Why do you need a family? What’s insufficient about just using allocsize(idx),
as used by __attribute__((alloc_size(...)) in GNU C? (Which you acknowledge the
existence of, but don’t justify why you need your own attribute.) What to use as
the allocator “family” string for C++ operator new seems pretty unclear too, so
I’m not sure how good an idea this free-form string argument is in your
proposal.

>  * `releaseptr(idx)`: Indicates that the function releases the pointer that
is its Nth argument.

This should probably just be free(idx)/frees(idx)/willfree(idx) (we already have
nofree as an attribute for arguments), or an attribute on the argument itself.
Talking about releasing makes it sound like reference counting semantics.

Jess

> These attributes, combined with the existing `allocsize(n[, m])` attribute
lets us annotate alloc, realloc, and free type functions in LLVM IR, which
relieves Rust of the need to carry a patch to describe its allocator functions
to LLVM’s optimizer. Some example IR of what this might look like:
>
> ; Function Attrs: nounwind ssp
> define i8* @test5(i32 %n) #4 {
> entry:
>   %0 = tail call noalias dereferenceable_or_null(20) i8* @malloc(i32 20) #8
>   %1 = load i8*, i8** @s, align 8
>   call void @llvm.memcpy.p0i8.p0i8.i32(i8* noundef nonnull align 1
dereferenceable(10) %0, i8* noundef nonnull align 1 dereferenceable(10) %1, i32
10, i1 false) #0
>   ret i8* %0
> }
>
> attributes #8 = { nounwind allocsize(0)
"allocator"="malloc" }
>
> Similarly, the call `free(foo)` would get the attributes
`”allocator”=”malloc” releaseptr(1)` and `realloc(foo, N)` gets
`”allocator”=”malloc” releaseptr(1) allocsize(1)`. Note that the `releaseptr(n)`
attribute is 1-indexed to avoid issues with storing zero values in attributes in
my current draft - I’m very open to suggestions to change that, this just seemed
like the right solution rather than adding getters/setters everywhere to
increment/decrement a value.
>
> Benefits
> ======> In addition to the benefits for Rust, the LLVM optimizer could
also be improved to not optimize away defects like
>
> {
>   auto *foo = new Thing();
>   free(foo);
> }
>
> which would then correctly crash instead of silently “working” until
something actually uses the allocation. Similarly, there’s a potential defect
when only one side of an overridden operator::new and operator::delete is
visible to the optimizer and inlineable, which can look indistinguishable from
the above after inlining.
>
> This also probably opens the door to fixing issues like
https://bugs.llvm.org/show_bug.cgi?id=49022 caused by overloading the `builtin`
annotation on allocator functions, but I’m unlikely to continue in that
direction.
>
> What do people think?
>
> Thanks,
> Augie
>
> [0]
https://github.com/rust-lang/llvm-project/commit/b1f55f7159540862c407a2d89d49434ce65892e5
> [1]
https://github.com/llvm/llvm-project/blob/cd5f582c3dd747ab97b57df37642b0dffba398ee/llvm/lib/Analysis/MemoryBuiltins.cpp#L73
> [2]
https://github.com/llvm/llvm-project/blob/cd5f582c3dd747ab97b57df37642b0dffba398ee/llvm/lib/Analysis/MemoryBuiltins.cpp#L433
>
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev