Tramboi
Tramboi

Reputation: 161

Get a label address out of the function scope in gcc/clang (C++)


I'm making some kind of interpreter and I'm computing a static const jump table thanks to local label addresses.
You know the drill, static const int JUMP_TABLE[] = { &&case0 - &&case0, &&case1 - &&case0 and so on.
For various reasons, mostly performance, I'd like to copy/compress this table in an object during init.
I'm hurting my head against the wall because I can't figure how to escape the lexical scoping of the function !

How can I somehow reference &&case0 from another function ?

Does somebody have a good trick for this ?
Thanks in advance

Upvotes: 7

Views: 932

Answers (2)

ron
ron

Reputation: 186

Sometimes Goto is simply the best solution. Tho very rare. Its still part of c++ after many years for a good reason

So what i did was create a global bool and set it after I initialize my address array. So the first time my interpreter function is called, it loads a structure of addresses so everything is within the same function.

Then with a little study of the assemblers output i was able to save a few ticks by arranging my code as follows.

if(is_initialized) .. ex command else ... initialize stuff. Goto ex command

use a goto to jump back to the top and execute the command. My interpreter is using almost 200 commands.

With a switch statement it was taking 5-1100 ticks. Depending on how far down the list the command was

Using goto functions[command] has it down to 14 regardless of where the command is in the list

This provides a method that is purely c++ but not supported on all compilers

Upvotes: 0

yugr
yugr

Reputation: 21916

I'm not aware of ways to achieve this within pure GNU C so approaches below use other mechanisms.

Double compilation

You can compile your object file twice, collecting offsets on the first run and using them on the second. For example

int foo(int x) {
#ifdef GENERATE_ADDRESSES
    static __attribute__((section(".foo_offsets"))) unsigned offsets[] = { &&case0 - &&case0, &&case1 - &&case0 };
#endif
    switch (x) {
case0:
        case 0:
            return 1;
case1:
        case 1:
            return 2;
    }
    return 0;
}

Now you can compile, extract bytes from .foo_offsets section and embed them to you app on second run

$ gcc tmp.c -c -DGENERATE_ADDRESSES
$ objcopy -j .foo_offsets -O binary tmp.o
$ xxd -i tmp.o | tee offsets.inc
unsigned char tmp_o[] = {
  0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00
};
unsigned int tmp_o_len = 8;

Inline assembly

You can use inline assembly to globalize labels:

extern char foo_case0[];
extern char foo_case1[];
const void *foo_addresses[] = { &foo_case0[0], &foo_case1[0] };

int foo(int x) {
    switch (x) {
        case 0:
asm("foo_case0:");
            return 1;
        case 1:
asm("foo_case1:");
            return 2;
    }
    return 0;
}

Unfortunately in this case you can only collect addresses (not offsets) so you'll need to manually compute offsets at startup.

Upvotes: 5

Related Questions