I know everyone hates gotos. In my code, for reasons I have considered and am comfortable with, they provide an effective solution (ie I'm not looking for "don't do that" as an answer, I understand your reservations, and understand why I am using them anyway).
So far they have been fantastic, but I want to expand the functionality in such a way that requires me to essentially be able to store pointers to the labels, then go to them later.
If this code worked, it would represent the type of functionality that I need. But it doesn't work, and 30 min of googling hasn't revealed anything. Does anyone have any ideas?
int main (void)
{
int i=1;
void* the_label_pointer;
the_label:
the_label_pointer = &the_label;
if( i-- )
goto *the_label_pointer;
return 0;
}
label: | goto label; In the above syntax, the first line tells the compiler to go to or jump to the statement marked as a label. Here label is a user-defined identifier which indicates the target statement. The statement immediately followed after 'label:' is the destination statement.
Explanation. A label is an identifier followed by a colon ( : ) and a statement (until C23). Labels are the only identifiers that have function scope: they can be used (in a goto statement) anywhere in the same function in which they appear. There may be multiple labels before any statement.
Short answer: no.
goto label; A label's name is a user defined identifier and is distinguished by the colon that immediately follows its name. The statement immediately followed after “label:” is the statement to be executed after goto statement. The goto statement jump to the statement marked with a label.
The C and C++ standards do not support this feature.
However, the GNU Compiler Collection (GCC) includes a non-standard extension for doing this, as described in the Labels as Values section of the Using the GNU Compiler Collection manual.
Essentially, they have added a special unary operator &&
that reports the address of the label as type void *
. See the article for details. With that extension, you could just use &&
instead of &
in your example, and it would work on GCC.
P.S. I know you don’t want me to say it, but I’ll say it anyway… DON’T DO THAT!!!
I know the feeling then everybody says it shouldn't be done; it just has to be done. In GNU C use &&the_label;
to take the address of a label. (https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html) The syntax you guessed, goto *ptr
on a void*
, is actually what GNU C uses.
Or if you want to use inline assembly for some reason, here's how to do it with GNU C asm goto
// unsafe: this needs to use asm goto so the compiler knows
// execution might not come out the other side
#define unsafe_jumpto(a) asm("jmp *%0"::"r"(a):)
// target pointer, possible targets
#define jumpto(a, ...) asm goto("jmp *%0" : : "r"(a) : : __VA_ARGS__)
int main (void)
{
int i=1;
void* the_label_pointer;
the_label:
the_label_pointer = &&the_label;
label2:
if( i-- )
jumpto(the_label_pointer, the_label, label2, label3);
label3:
return 0;
}
The list of labels must include every possible value for the_label_pointer
.
The macro expansion will be something like
asm goto("jmp *%0" : : "ri"(the_label_pointer) : : the_label, label2, label3);
This compiles with gcc 4.5 and later, and with the latest clang which just got asm goto
support some time after clang 8.0. https://godbolt.org/z/BzhckE. The resulting asm looks like this for GCC9.1, which optimized away the "loop" of i=i
/ i--
and just put the the_label
after the jumpto
. So it still runs exactly once, like in the C source.
# gcc9.1 -O3 -fpie
main:
leaq .L2(%rip), %rax # ptr = &&label
jmp *%rax # from inline asm
.L2:
xorl %eax, %eax # return 0
ret
But clang didn't do that optimization and still has the loop:
# clang -O3 -fpie
main:
movl $1, %eax
leaq .Ltmp1(%rip), %rcx
.Ltmp1: # Block address taken
subl $1, %eax
jb .LBB0_4 # jump over the JMP if i was < 1 (unsigned) before SUB. i.e. skip the backwards jump if i wrapped
jmpq *%rcx # from inline asm
.LBB0_4:
xorl %eax, %eax # return 0
retq
The label address operator && will only work with gcc. And obviously the jumpto assembly macro needs to be implemented specifically for each processor (this one works with both 32 and 64 bit x86).
Also keep in mind that (without asm goto
) there would be no guarantee that the state of the stack is the same at two different points in the same function. And at least with some optimization turned on it's possible that the compiler assumes some registers to contain some value at the point after the label. These kind of things can easily get screwed up then doing crazy shit the compiler doesn't expect. Be sure to proof read the compiled code.
These are why asm goto
is necessary to make it safe by letting the compiler know where you will / might jump, getting consistent code-gen for the jump and the destination.
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