J
James Kuyper
Concrete example:
int quux(void)
{
int foo = 42;
int *bar = &foo;
return foo;
}
1. Both foo and bar start out in memory.
2. foo can't be migrated to a register because of the &foo.
3. bar is eliminated via dead code elimination.
4. foo can now be migrated to a register.
5. No address is needed for either foo or bar.
A prime example of a situation where neither foo, nor bar, nor the
address of foo, are needed.
OTOH, say you have this:
#include <stdio.h>
int quux(void)
{
int foo = 42;
int *bar = &foo;
printf("%p\n", (void*)bar);
return foo;
}
1. Both foo and bar start out in memory.
2. foo can't be migrated to a register because of the &foo.
3. bar is migrated to a register.
4. An address is needed for foo but not for bar.
Can you give a similar example for what you're describing--and your
mental model of what a compiler would do with it?
Yes, the second case you've just given fits what I'm talking about
exactly. Having recognized that there's absolutely no need to store 42
anywhere, the compiler reserves an arbitrary address for foo, but never
bothers storing anything at that address. That address is stored in a
register and passed to printf(), and is used for no other purpose. The
compiler need only ensure that the address is not used for any other
object that might have a lifetime that overlaps the call to quux(),
which is something it would have had to do anyway, if 42 had been stored
there.
If foo had static storage duration, there would be the possibility that
some other part of the code could ask the user to give foo's address
back to it, and then dereference a pointer to foo, which would make this
no longer a valid optimization. Because foo actually has automatic
storage duration, there's no possible way to write code which is
guaranteed to use that address during foo's lifetime, not even with
multi-threaded code.
Note: I'm winging it on that last statement. I've not had time to
assimilate C's new support for multi-threaded code. If there's some way
to guarantee that a scanf() call followed by a dereference in some other
part of the program will be executed between the printf() and the return
in quux(), then I'm wrong. But I'd expect that the ability to guarantee
such sequencing would require executing a call to one of the new
thread-related functions between the printf() and the return. Code where
such sequencing is merely possible, but not guaranteed, has undefined
behavior if it doesn't execute in that sequence, thereby allowing the
optimization.