Last year, I asked on the mailing list [0] to clarify which are the assumptions that LLVM makes when llvm.memcpy/memmove/memset.* (the intrinsics, not the C functions) are called with a count of 0 -- including the case where the count is not statically known, but happens to be 0 at run-time. The concept of a "dangling" pointer does not make much sense for size 0 (by definition, all the bytes it points to are allocated because it points to no bytes). However, some questions remain open. Specifically: - Is it okay for some of the pointers to be NULL? - Is it okay for some of the pointers to not be aligned, even though the annotation says they should be aligned? Having definite answers to such questions is important for us in Rust, so that we can settle in rules for our intrinsics that compile to LLVM's. I hope the "Documentation" product is the right one for such issues. [0] https://lists.llvm.org/pipermail/llvm-dev/2017-July/115665.html
The answer probably should be that a null/invalid pointer is okay, but violating the align attribute is not. Justification: if the size is zero, the intrinsics don't perform any memory operations, so the pointer doesn't have to point to anything in particular. And "align" is a generic parameter attribute which should have the same meaning everywhere. (If there's a significant optimization justification, we could possibly define the "align" attribute to produce poison or something like that, but UB is more straightforward to reason about.) https://llvm.org/docs/LangRef.html#parameter-attributes should probably be fixed to explicitly say "if the pointer is not aligned, the behavior is undefined", like it does for other attributes like nonnull. And put a note on memcpy/memset/memmove noting that if the size is zero, the pointer doesn't have to point to valid memory. And maybe explicitly note that the rules for the "align" attribute are the same for memcpy/memset/memmove as they are for other calls. If you're interested, you can try writing a patch for LangRef yourself (it's docs/LangRef.rst in the LLVM repository). Or I'll probably get to it at some point otherwise.
Thanks! I am wondering, why do you treat the non-nullness separate from alignedness? I understand the reasoning for why the pointer must be aligned even for no access, but I think the same reasoning applied to being NULL.
The "align" attribute is generic; it needs to have the same meaning in all contexts. And the simplest useful way to define for other contexts is "the bottom N bits are zero, or the behavior is undefined". If the arguments to memcpy were marked "nonnull", it would similarly be UB to pass a null pointer. But they aren't, generally, so there isn't any particular reason to treat null specially.
I see, this makes sense. In Rust, I usually like to treat "aligned and non-NULL" together; this would be the first and only case where the two requirements go separate. So to be safe we should likely just require both.
I submitted a patch at https://reviews.llvm.org/D57600. That's quite a bit more complicated than one is used to nowadays, so I hope I didn't make a mistake.
Landed the documentation updates as r354911 (GitHub monorepo commit 76eb4b02d93b3a7704b496f3e16dd14bf72cb3a9).