In GCC you can use a computed goto by taking the address of a label (as in void *addr = &&label
) and then jumping to it (jump *addr
). The GCC manual says you can jump to this address from anywhere in the function, it's only that jumping to it from another function is undefined.
When you jump to the code it cannot assume anything about the values of registers, so presumably it reloads them from memory. However the value of the stack pointer is also not necessarily defined, for example you could be jumping from a nested scope which declares extra variables.
The question is how does GCC manage to set to value of the stack pointer to the correct value (it may be too high or too low)? And how does this interact with -fomit-frame-pointer
(if it does)?
Finally, for extra points, what are the real constraints about where you can jump to a label from? For example, you could probably do it from an interrupt handler.
In general, when you have a function with labels whose address is taken, gcc needs to ensure that you can jump to that label from any indirect goto in the function -- so it needs to layout the stack so that the exact stack pointer doesn't matter (everything is indexed off the frame pointer), or that the stack pointer is consistent across all of them. Generally, this means it allocates a fixed amount of stack space when the function starts and never touches the stack pointer afterwards. So if you have inner scopes with variables, the space will be allocated at function start and freed at function end, not in the inner scope. Only the constructor and destructor (if any) need to be tied to the inner scope.
The only constraint on jumping to labels is the one you noted -- you can only do it from within the function that contains the labels. Not from any other stack frame of any other function or interrupt handler or anything.
edit
If you want to be able to jump from one stack frame to another, you need to use setjmp/longjmp or something similar to unwind the stack. You could combine that with an indirect goto -- something like:
if (target = (void *)setjmp(jmpbuf)) goto *target;
that way you could call longjmp(jmpbuf, label_address);
from any called function to unwind the stack and then jump to the label. As long as setjmp/longjmp
works from an interrupt handler, this will also work from an interrupt handler. Also depends on sizeof(int) == sizeof(void *)
, which is not always the case.
I don't think that the fact that the goto's are computed add to the effect that it has on local variables. The lifetime of local variable starts from entering their declaration at or beyond their declaration and ends when the scope of the variable cannot be reached in any way. This includes all different sorts of control flow, in particular goto
and longjmp
. So all such variables are always safe, until the return from the function in which they are declared.
Labels in C are visible to the whole englobing function, so it makes not much difference if this is a computed goto
. You could always replace a computed goto
with a more or less involved switch
statement.
One notable exception from this rule on local variables are variable length arrays, VLA. Since they do necessarily change the stack pointer, they have different rules. There lifetime ends as soon as you quit their block of declaration and goto
and longjmp
are not allowed into scopes after a declaration of a variably modified type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With