Optimizing memset, in particular, can reap huge benefits, for example for sizes of 4 or 8 bytes in tight loops or for zeroing cache line sized blocks.
The semantics of memset and memset_s are defined by a standard. They're not unknown functions that could do anything, and they're breaking a rule by treating them differently, like you're suggesting they are.
Compilers do have an intrinsic equivalent but that has nothing to do with whether it gets optimized out or kept.
Following SSA rules, if you are writing to a non-volatile memory and not reading from it for the remaining duration of its lifetime then it's dead code.
use memset_s was a suggestion if your goal is to scrub memory because it guarantees the write will happen. The guarantee comes from the C standard:
> Unlike memset, any call to the memset_s function shall be evaluated strictly according to the rules of the abstract machine as described in (5.1.2.3). That is, any call to the memset_s function shall assume that the memory indicated by s and n may be accessible in the future and thus must contain the values indicated by c
Which essentially means treat the memory as-if it's volatile.
If you pass a pointer to a volatile or atomic object to another function the compiler cannot see into, the compiler knows that any specified operations might be observable, so must act accordingly. Then, operations on that object will not be optimized away. Or, anyway, not until someone builds the program using Link Time Optimization ("-flto") and the called function body becomes visible.
Calling C11's memset_s, or MS's SecureZeroMemory, or FreeBSD's explicit_bzero, has the effect, to the compiler, of making the object observable, so that operations on it will not be optimized away. There is no way to express this locally within the language; you must rely on facilities provided by a Standard or your platform, or on the dodgy linkage trick cited elsewhere.
However, memset's declaration takes a void* which prevents you from passing a volatile void*. casting or not won't change anything in this case because what matters is what memset takes and not what the pointer's fully qualified type is prior to calling memset.
From the compiler's perspective, you're passing a non-volatile pointer to memset which makes it a candidate for elimination if the pointer's lifetime ends after memset call.
[1]: https://lore.kernel.org/lkml/Pine.LNX.4.64.0607060856080.124...
Many people mentally model atomic as akin to volatile, about which they also frequently harbor superstitions.
I knew a FreeBSD core committer who insisted (and convinced our boss!) that volatile was entirely sufficient to serialize interactions between threads.
Someone else expected a line with a volatile or atomic operation never to be optimized away, and was appalled that it happened. Assignment to a volatile stack object whose address has not escaped and is not used will, in fact, be optimized away, regardless of the "clear language of the Standard".
Often, not performing what would appear to be a pointless optimization would block a more substantial optimization. Compiler writers optimize aggressively in order to break through to the substantial ones, which are most usually dead-code elimination that can then propagate backward and upward.
If you really need for a volatile object and operations on it not to be optimized away, you need to allow a pointer to it to escape to somewhere the compiler cannot see. Have a function in a separate ".o" file, and pass the pointer to that. Or, better, use a compiler intrinsic or other facility provided specifically for the purpose, such as memset_s or explicit_bzero.
> Assignment to a volatile stack object whose address has not escaped and is not used will, in fact, be optimized away
I don't believe either of these statements. https://godbolt.org/z/8cqEdY9oE is a simple case where this doesn't happen. Can you post a Godbolt link or something that demonstrates when it does?
Keep in mind that the standard says "Accesses to volatile objects are evaluated strictly according to the rules of the abstract machine." In particular, it doesn't say that accesses to non-volatile objects through volatile-qualified pointers will be handled in any special way. (This looks like it's going to be changed in C2x, but it hasn't been changed as of C17.) Also, it says "If an attempt is made to refer to an object defined with a volatile-qualified type through use of an lvalue with non-volatile-qualified type, the behavior is undefined."
Talk to any core compiler implementer, and they will say that the optimization is allowed. No compiler performs all permitted optimizations, in any release, but the set of those each does do, varies.
Can you provide a different program, compiler/version, and command line where it will be optimized away?
> Talk to any core compiler implementer, and they will say that the optimization is allowed.
Are you just guessing that they'll say this, or have they actually said this? If the latter, can you link to where they did?
> No compiler performs all permitted optimizations, in any release, but the set of those each does do, varies.
My Godbolt link is a really obvious, easy place that the optimization you claim is possible could be performed. I think the fact that none of the 4 major compilers optimized it away is decent evidence that it isn't allowed.
They have said it face-to-face, in person. It is possible that, with enough pushback, they have since disabled the optimization. (They all work at large corporations, nowadays, and must obey management direction.) Or, more likely, disabled certain cases of it. I will ask again.
For documentary evidence, you may consult the definition of C11 memset_s, which is specified not to permit the optimization. It is hard to imagine such a requirement being perceived as needed, absent cases where calls to memset have actually been elided.
I will also experiment further with Godbolt. You might have failed to tickle the compilers in just the right way.
If you do ask again, can you do so somewhere like a public mailing list, so I can read their response myself?
> For documentary evidence, you may consult the definition of C11 memset_s, which is specified not to permit the optimization. It is hard to imagine such a requirement being perceived as needed, absent cases where calls to memset have actually been elided.
The point of memset_s is to prohibit the optimization when the object isn't volatile.
99% of C/C++ programmers are not bored language lawyers with time to memorize thousand page specs. They use the language because they're pragmatic. Messing up peoples code with incredibly over-eager optimization is the opposite of pragmatism. I'd rather spend a day writing inline assembly for a hot path than two days dealing with the compiler breaking my code and making users data insecure becuase "usually" its ok to ignore a memset.