-
Notifications
You must be signed in to change notification settings - Fork 58
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
What memory is the Global allocator allowed to access #534
Comments
That's a good observation!
Yes I would say this is what happens.
|
So what happens if you implement malloc in Rust, perhaps part of relibc or some other libc implemented in Rust? How should it return memory not part of Rust reachable memory? Or what happens in a kernel where there you allocate pages from physical RAM, mapping them into virtual memory? Or in embedded where there isn't even an MMU? To me it seems quite suspect in a systems programming language to treat any function as magic that isn't implemented by the compiler itself. (I.e. anything that isn't a lang item, or possibly things from compiler-builtins, many (most?) of the latter don't even have special opsem semantics but are about things like softfloat). |
Yeah that's a good question. I think generally the answer is, that's basically an FFI call, so these are two independent instances of the Rust Abstract Machine that each have their own idea of what memory is "Rust memory". (Note that this is specifically about
Not sure how that's related to this discussion. The only times anything special happens is when you call I don't think it is useful to expand the scope of this issue to "everything related to allocation ever", as that's a lot of stuff. So lets focus on the special magic allocation functions that your post discusses.
We inherited this from C, so please redirect these comments to the responsible parties. ;) The reason they did this, of course, is that it's good for optimizations, and many people would complain loudly if we stopped doing these optimizations. |
That can be defensible if we have two separate cdylib or cstaticlibs. What if they are all part of the same cargo compilation (or in the extreme case, the same crate)? How do you draw the border between the AM instances? Presumably both could share access to some resources then without going through the alloc interface. Is that UB? Let's consider a concrete example. In a piece of embedded code you have a static memory block that you satisfy your dynamic allocations from. You use this directly sometimes from Rust, but you have a C dependency and you want to provide a malloc compatibility layer for that piece of C code. Is this OK from an opsem perspective? What if some rust code allocated via this malloc in Rust as well? |
How would you even do that? I don't know when exactly the special The only thing that could become a problem here is if LLVM later decides to inline this symbol during LTO. I don't know LLVM enough to know what it will and won't do here. And yes, sharing of resources between the allocator and the rest of the program is extremely limited. LLVM sadly does not document exactly how, so we'll have to figure that out. |
malloc
In fact, this issue is a mess because you mixed up two questions that are unfortunately independent -- the first part of your question is part of #442, the second part is specifically about Please open a new issue specifically for |
malloc
I opened an issue for |
Consider the following code:
Right now
src()
gets optimized to return0
unconditionally after freeing the memory backingx
. Furthermore, the write of the mutable reference gets optimized out, since the compiler assumes that deallocator doesn't access the freed memory block w/o first overwriting it.I believe you could justify these semantics if you specify that
alloc::dealloc
overwrites the underlying buffer with undef/poison before providing it to the global allocator, however this should probably be documented in the docs (unless it's "obvious" that the Global allocator can't do that, since that's probably an expectation that most people would have).Furthermore, consider the example from rust-lang/rust#130853. I'm not sure how to justify the original transformation w/ Stacked or Tree borrows, however, I was wondering if the following is justifiable:
Currently, LLVM doesn't do the second optimization. However, it does perform it if you manually set
System
to be the global allocator: https://rust.godbolt.org/z/a77PWjeKE 1. This is due to this line, which is used by their GVN pass.TLDR: is the implementor of the global allocator required to not modify references that are "visible"2 in code that invokes the global allocator methods? I realize that that definition is kinda scuffed, but that's how LLVM explains their assumptions of
malloc/calloc/free
:Footnotes
You also get the
malloc
->calloc
transformation for types other than these hardcoded ones if you setSystem
to be the global allocator manually. ↩This probably means that allocator methods aren't allowed to be inlined if the optimizer wants to make assumptions about code that invokes them. ↩
The text was updated successfully, but these errors were encountered: